2012-10-05 15 views
5

Il mio caso d'uso è di mantenere una cache in memoria sui dati memorizzati in un DB persistente.Implementazione di una cache periodicamente aggiornata in Java

Uso i dati per compilare una lista/mappa di voci sull'interfaccia utente. In qualsiasi momento, i dati visualizzati sull'interfaccia utente dovrebbero essere il più aggiornati possibile (beh, questo può essere fatto dalla frequenza di aggiornamento della cache).

La differenza principale tra un'implementazione regolare della cache e questa particolare cache è che ha bisogno di un aggiornamento di massa di tutti gli elementi a intervalli regolari e quindi è piuttosto diverso da un tipo di cache LRU.

Ho bisogno di fare questa implementazione in Java e sarà fantastico se ci sono dei framework esistenti che possono essere utilizzati per ottenere questo costruito attorno ad essi.

Ho esplorato la libreria cache di Google Guava ma è più adatto a un aggiornamento per voce piuttosto che a un aggiornamento in blocco. Non ci sono semplici API che eseguono un aggiornamento sull'intera cache.

Qualsiasi aiuto sarà molto apprezzato.

Inoltre, se è possibile eseguire l'aggiornamento in modo incrementale, sarà grande perché l'unica limitazione che si verifica durante l'aggiornamento dell'intera cache è che se la cache è di dimensioni molto grandi, l'heap della memoria deve essere almeno due volte la dimensione della cache per caricare le nuove voci e sostituire la vecchia mappa con quella nuova. Se la cache è incrementale o c'è un aggiornamento a blocchi (aggiornamento in dimensioni uguali) sarà fantastico.

risposta

3

EHCache è una libreria bella full-optional Java caching. immagino che abbiano qualcosa che possa funzionare per te.

Per eseguire un ricaricamento incrementale di una cache (che funzionerebbe sulla maggior parte delle cache), è sufficiente scorrere le voci attualmente caricate e forzarle ad aggiornarle. (è possibile eseguire questa attività su un programma di pianificazione in background).

In alternativa a forzare l'intera cache a ricaricare, EHCache ha la possibilità di specificare un "time-to-live" per una voce, in modo che le voci vengano ricaricate automaticamente se sono obsolete.

+1

@ jtahlborn- L'API BulkLoader (http://ehcache.org/documentation/apis/bulk-loading) di EhCache è utile ma sarebbe stato grandioso se avesse fornito un'opzione refreshTime o intervallo periodico in cui avrebbe gestito da sola la pianificazione per l'aggiornamento della cache. In ogni caso, può sempre essere ottenuto tramite un programma di pianificazione esterno e invocando periodicamente l'API di caricamento collettivo. Grazie per la risposta. –

+0

Per informazioni su EHCache: http://www.ehcache.org/documentation/3.3/thread-pools.html e http://terracotta.org/documentation/4.1/bigmemorymax/api/bulk-loading – Aliuk

+0

Ma. .non è time-to-live rimuove semplicemente l'elemento dalla cache? NON è la stessa cosa che hai scritto qui - "ricarica automatica" – javagirl

0

Proprio ereditano questa classe, e attuare loadDataFromDB e UpdateData come si desidera ottenere gli aggiornamenti incremential

import org.apache.log4j.Logger; 
import java.util.List; 
import java.util.concurrent.Semaphore; 


public abstract class Updatable<T> 
{ 
    protected volatile long lastRefreshed = 0; 
    private final int REFRESH_FREQUENCY_MILLISECONDS = 300000; // 5 minutes 
    private Thread updateThread; 
    private final Semaphore updateInProgress = new Semaphore(1); 

    protected static final Logger log = Logger.getLogger(Updatable.class); 

    public void forceRefresh() 
    { 
     try 
     { 
      updateInProgress.acquire(); 
     } 
     catch (InterruptedException e) 
     { 
      log.warn("forceRefresh Interrupted"); 
     } 

     try 
     { 
      loadAllData(); 
     } 
     catch (Exception e) 
     { 
      log.error("Exception while updating data from DB", e); 
     } 
     finally 
      { 
      updateInProgress.release(); 
     } 

    } 

    protected void checkRefresh() 
    { 
     if (lastRefreshed + REFRESH_FREQUENCY_MILLISECONDS <  System.currentTimeMillis()) 
      startUpdateThread(); 
    } 

    private void startUpdateThread() 
    { 
     if (updateInProgress.tryAcquire()) 
     { 
      updateThread = new Thread(new Runnable() 
      { 
       public void run() 
       { 
        try 
        { 
         loadAllData(); 
        } 
        catch (Exception e) 
        { 
         log.error("Exception while updating data from DB", e); 
        } 
        finally 
        { 
         updateInProgress.release(); 
        } 
       } 
      }); 

      updateThread.start(); 
     } 
    } 

    /** 
    * implement this function to load the data from DB 
    * 
    * @return 
    */ 
    protected abstract List<T> loadFromDB(); 

    /** 
    * Implement this function to hotswap the data in memory after it was loaded from DB 
    * 
    * @param data 
    */ 
    protected abstract void updateData(List<T> data); 

    private void loadAllData() 
    { 
     List<T> l = loadFromDB(); 
     updateData(l); 
     lastRefreshed = System.currentTimeMillis(); 
    } 

    public void invalidateCache() 
    { 
     lastRefreshed = 0; 
    } 

} 
+0

Grazie per la risposta RA. Quando viene chiamata la funzione checkRefresh()? Se ho capito bene, questo avrebbe bisogno di un processo continuo in esecuzione per sondare utilizzando checkRefresh a intervalli regolari. Non vedevo l'ora di realizzare un'implementazione più pulita in cui potevo semplicemente installare una nuova cache con un caricatore di cache. –

+0

CheckRefresh dovrebbe essere chiamato in ogni operazione get implementata nella classe. IO.E: public Data get() {checkRefresh(); // restituisce i dati; } –

+0

Ma ciò influirà sulla latenza del recupero dei dati nei casi in cui il thread di aggiornamento viene attivato dove, come se fosse andato avanti come un cron e già prefetched i dati, un caso del genere non sarebbe sorto. –

0

Una cosa che deve essere controllata è che è necessario un aggiornamento periodico? È possibile applicare la logica di aggiornamento una volta recuperati i dati dalla cache, questo eliminerebbe la necessità di qualsiasi aggiornamento asincrono e rimuoverebbe la necessità di mantenere le vecchie copie della cache. Questo IMO è il modo più semplice e migliore per aggiornare i dati della cache in quanto non comporta alcun overhead aggiuntivo.

T getData(){ 
     // check if the last access time + refresh interval >= currenttime if so then refresh cache 
    // return data 
    } 

Questo farà sì che i dati vengono aggiornati sulla base all'intervallo di aggiornamento e non ha bisogno di alcun aggiornamento asincrono.