Sto cercando di capire il modo migliore per capire come memorizzare nella cache gli oggetti del dominio

4

Ho sempre sbagliato, sono sicuro che anche molti altri hanno un riferimento tramite una mappa e scrivono in DB ecc.

Ho bisogno di farlo bene, e non so proprio come farlo. So come voglio che i miei oggetti siano memorizzati nella cache ma non sono sicuro su come ottenerlo. Ciò che complica le cose è che devo fare questo per un sistema legacy in cui il DB può cambiare senza preavviso alla mia applicazione.

Quindi nel contesto di un'applicazione web, supponiamo di avere un WidgetService che ha diversi metodi:

Widget getWidget();
Collection<Widget> getAllWidgets();
Collection<Widget> getWidgetsByCategory(String categoryCode);
Collection<Widget> getWidgetsByContainer(Integer parentContainer);
Collection<Widget> getWidgetsByStatus(String status);

Dato questo, potrei decidere di memorizzare nella cache con la firma del metodo, cioè getWidgetsByCategory("AA") avrebbe una singola voce di cache, o potrei citare i widget individualmente, il che sarebbe difficile, credo; OPPURE, una chiamata a qualsiasi metodo dovrebbe prima memorizzare nella cache TUTTI i widget con una chiamata a getAllWidgets() ma getAllWidgets() produrrebbe cache che corrispondono a tutte le chiavi per le altre chiamate di metodo. Ad esempio, prendi il seguente codice teorico non testato.

Collection<Widget> getAllWidgets() {
    Entity entity = cache.get("ALL_WIDGETS");
    Collection<Widget> res;
    if (entity == null) {
        res = loadCache();
    } else {
        res = (Collection<Widget>) entity.getValue();
    }
    return res
}

Collection<Widget> loadCache() {
    // Get widgets from underlying DB
    Collection<Widget> res = db.getAllWidgets();
    cache.put("ALL_WIDGETS", res);
    Map<String, List<Widget>> byCat = new HashMap<>();
    for (Widget w : res) {
        // cache by different types of method calls, i.e. by category
        if (!byCat.containsKey(widget.getCategory()) {
            byCat.put(widget.getCategory(), new ArrayList<Widget>);
        }
        byCat.get(widget.getCatgory(), widget);
    }
    cacheCategories(byCat);
    return res;
}

Collection<Widget> getWidgetsByCategory(String categoryCode) {
    CategoryCacheKey key = new CategoryCacheKey(categoryCode);
    Entity ent = cache.get(key);
    if (entity == null) {
        loadCache();
    }
    ent = cache.get(key);
    return ent == null ? Collections.emptyList() : (Collection<Widget>)ent.getValue();
}

NOTA : Non ho lavorato con un gestore cache, il codice sopra illustra cache come un oggetto che può contenere cache per coppie chiave / valore, sebbene non sia modellato su alcuna implementazione specifica.

Usando questo ho il vantaggio di essere in grado di memorizzare nella cache tutti gli oggetti nei diversi modi in cui verranno chiamati con solo singoli oggetti nell'heap, mentre se dovessi memorizzare nella cache l'invocazione di chiamata del metodo tramite dire Spring, sarebbe (credo ) memorizza nella cache più copie degli oggetti.

Desidero davvero provare a capire i modi migliori per memorizzare nella cache gli oggetti del dominio prima di intraprendere il percorso sbagliato e renderlo più difficile per me stesso in seguito. Ho letto la documentazione sul sito web di Ehcache e ho trovato vari articoli di interesse, ma nulla per dare una buona tecnica solida.

Dato che sto lavorando con un sistema ERP, alcune chiamate DB sono molto complicate, non che il DB sia lento, ma la rappresentazione aziendale degli oggetti di dominio lo rende molto goffo, in aggiunta al fatto che ci sono in realtà 11 diversi Nei database in cui è possibile contenere informazioni che questa applicazione si sta consolidando in un'unica vista, ciò rende la cache molto importante.

    
posta Brett Ryan 29.11.2012 - 08:44
fonte

1 risposta

2

Per essere utile, una cache deve fornire l'accesso ai dati più velocemente di quanto possa essere recuperato dal database. Dato che la maggior parte delle chiamate al database implicano un roundtrip della rete, è opportuno memorizzare nella cache ogni volta che è noto che l'output (valore) non cambierà per lo stesso input (chiave) su una dimensione nota (tempo, dimensione, ecc.).

Quindi se gli input sono imprevedibili, o indecifrabili, quindi non possono essere associati a una singola chiave, la cache potrebbe non essere la soluzione migliore: finirai per cercare di scrivere (e mantenere) un database di scarsa qualità. Investire i soldi in una rete più veloce, invece.

Non importa se la tua cache contiene più di una copia di un particolare oggetto, purché la chiave sia utile per un consumatore a valle. L'obiettivo della cache è quello di migliorare le prestazioni per i diversi consumatori dei dati che possono avvicinarsi al set di dati sottostante da diversi punti di vista (ad esempio Customer-centric o Invoice-centric).

In termini di implementazione su piccola scala, potresti dare un'occhiata alle classi della cache in Guava libreria anziché EhCache. Un tipico esempio di cache di compilazione automatica potrebbe essere:

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build(
           new CacheLoader<Key, Graph>() {
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           });

Come puoi vedere, è molto semplice lavorare con Guava e fornisce una vasta gamma di strategie di sfratto della cache (dimensioni, riferimenti, età, ecc.). La libreria Guava fornisce anche una serie di utili classi di utilità che aumentano quelle che si trovano nel JDK, quindi il tuo codice di base complessivo ne trarrà vantaggio.

Quindi il tuo approccio per decorare i tuoi metodi DAO con una classe che combina una cache dedicata e il set di risultati DAO con una chiave derivata è valido. Ogni chiamata al metodo provoca una ricerca iniziale della chiave locale prima di restituire, che è ciò che si cerca di cercare. Accoppialo con un'appropriata strategia di espulsione messa a punto per ogni metodo e hai una soluzione semplice da comprendere e mantenere che dovrebbe scalare bene.

    
risposta data 30.11.2012 - 10:45
fonte

Leggi altre domande sui tag