2011-10-27 16 views
17

Ho un requisito per il caricamento di dati statici da un database da utilizzare in un'applicazione Java. Qualsiasi meccanismo di cache dovrebbe avere le seguenti funzionalità:Valori di precarica per una Guava Cache

  • Carica tutte dati statici dal database (una volta caricato, questi dati non cambieranno)
  • Carica nuovi dati dal database (dati presenti nel database al start non cambia, ma è possibile aggiungere nuovi dati)

Il caricamento lento di tutti i dati non è un'opzione in quanto l'applicazione verrà distribuita in più posizioni geografiche e dovrà comunicare con un singolo database. Il caricamento lento dei dati renderà la prima richiesta per un elemento specifico troppo lento dove l'applicazione si trova in una regione diversa rispetto al database.

Ho utilizzato l'API MapMaker in Guava con esito positivo, ma ora stiamo eseguendo l'aggiornamento all'ultima versione e non riesco a trovare la stessa funzionalità nell'API CacheBuilder; Non riesco a trovare un modo pulito per caricare tutti i dati all'avvio.

Un modo sarebbe caricare tutte le chiavi dal database e caricarle singolarmente attraverso la cache. Ciò funzionerebbe ma risulterebbe in chiamate N + 1 al database, che non è proprio la soluzione efficiente che sto cercando.

public void loadData(){ 
    List<String> keys = getAllKeys(); 
    for(String s : keys) 
     cache.get(s); 
} 

o l'altra soluzione è quella di utilizzare un'implementazione ConcurrentHashMap e gestire tutti i thread e le voci mancanti me stesso? Non sono entusiasta di farlo poiché le API di MapMaker e CacheBuilder forniscono gratuitamente il blocco del thread basato sulla chiave senza dover fornire test aggiuntivi. Sono anche abbastanza sicuro che le implementazioni di MapMaker/CacheBuilder avranno alcune efficienze che non conosco/non ho il tempo di investigare.

public Element get(String key){ 
    Lock lock = getObjectLock(key); 
    lock.lock(); 
    try{ 
     Element ret = map.get(key) 
     if(ret == null){ 
      ret = getElement(key); // database call 
      map.put(key, e); 
     } 
     return ret; 
    }finally { 
     lock.unlock(); 
    } 
} 

Qualcuno può pensare a una soluzione migliore alle mie due esigenze?


Feature Request

Non credo di pre-caricamento di una cache è un requisito raro, quindi sarebbe bello se il CacheBuilder fornito un'opzione di configurazione per precaricare la cache . Penso fornendo un'interfaccia (molto simile CacheLoader) che popolano la cache allo start-up sarebbe una soluzione ideale, come ad esempio:

CacheBuilder.newBuilder().populate(new CachePopulator<String, Element>(){ 

    @Override 
    public Map<String, Element> populate() throws Exception { 
     return getAllElements(); 
    } 

}).build(new CacheLoader<String, Element>(){ 

    @Override 
    public Element load(String key) throws Exception {  
     return getElement(key); 
    } 

}); 

Questa implementazione consentirebbe la cache per essere pre-popolato con tutti gli elementi rilevanti oggetti, mantenendo la sottostante CustomConcurrentHashMap non visibile al mondo esterno.

+0

Aggiungere la richiesta di funzionalità all'elenco dei problemi di Guava. –

+1

Aggiunto (numero 775) – Richard

+1

http://code.google.com/p/guava-libraries/issues/detail?id=775 –

risposta

3

Caricherò tutti i dati statici dal DB e li memorizzerò nella cache utilizzando cache.asMap().put(key, value) ([Guava 10.0.1 consente le operazioni di scrittura sulla vista Cache.asMap()] [1]).

Naturalmente, questi dati statici potrebbe ottenere sfrattati, se la cache è configurato per sfrattare le voci ...

L'CachePopulator idea è interessante.

+1

Funzionerebbe, ma sono obbligato a usare Guava 10.0.0, che usa il Implementazione di ComputingCache $ CacheAsMap che lancia una UnsupportedOperationException se viene chiamato uno dei metodi di modifica. Idealmente vorrei aggiornare, ma non è un'opzione al momento – Richard

+3

Beh, 10.0.1 era una piccola versione di bug fix il cui obiettivo era riattivare cache.asMap(). Put(). Se non riesci a fare un aggiornamento così piccolo, suppongo che tu sia brindata per ora ... –

+2

Grazie per il tuo ottimismo :) – Richard

6

Nel breve termine vorrei solo utilizzare Cache.asMap().putAll(Map<K, V>).

Una volta rilasciato Guava 11.0, è possibile utilizzare Cache.getAll(Iterable<K>), che emetterà un'unica richiesta in blocco per tutti gli elementi mancanti.

+10

Cache.getAll (Iterable ) non emette una singola richiesta di massa per gli elementi assenti. Secondo l'API, emetterà una sola chiamata per tutte le chiavi fornite, a meno che non venga superato. Per una discussione su questo argomento, consulta http://code.google.com/p/guava-libraries/issues/detail?can=2&q=775&colspec=ID%20Type%20Status%20Milestone%20Summary&id=775 – Richard

Problemi correlati