Impatto sulla memorizzazione nella cache NHibernate per le ricerche con risultati, incluso il valore calcolato mappato come formula (ad es. Classifica)

StackOverflow https://stackoverflow.com/questions/1627707

Domanda

Quando si definisce una proprietà calcolata utilizzando una formula in NHibernate, quali sono le implicazioni quando la formula varia il suo risultato a seconda delle restrizioni della query, in particolare per quanto riguarda la cache della query?

Più specificamente, considera la seguente semplice classe C #:

public class Entity
{
    public Entity() { }
    public virtual int Id { get; protected set; }
    public virtual string Key { get; protected set; }
    public virtual string Value { get; protected set; }
    public virtual int Rank { get; protected set; }
}

Mappato con la seguente semplice mappatura NHibernate:

<class name="Entity" mutable="false">
    <id name="Id">
        <generator class="native">
    </id>
    <property name="Key"/>
    <property name="Value"/>
    <property name="Rank" formula="row_number() over(order by value)">
</class>

Esecuzione con una factory di sessione con l'opzione hibernate.cache.use_query_cache impostata su true e interrogata nei seguenti modi:

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
IList<Entity> queryResult1 = criteria.List<Entity>();

criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.Add(Restrictions.Like("Key", "a", MatchMode.Anywhere));
IList<Entity> queryResult2 = criteria.List<Entity>();

Entity directResult = session.Load<Entity>(id);

NHibernate si comporterà in modo ragionevole per le Entità restituite? O potrebbe il "Rango" il valore di una query memorizzata nella cache inquina il valore di rango di un'altra query a causa della cache della query? Ci sono altre preoccupazioni quando si utilizza una formula simile nelle mappature di NHibernate?

EDIT:

Potrebbe anche valere la pena notare che nel mio caso particolare, "Entity" non è un'entità aziendale di prima classe, ma una sorta di meta-entità. Si associa a una vista di database indicizzata su altre entità di prima classe e viene utilizzato esclusivamente per la ricerca (la chiamata session.Load (id) è inventata e non dovrebbe mai effettivamente avvenire in pratica).

E, se ci sono implicazioni sulla memorizzazione nella cache, come sospetto, quali alternative potrebbero esistere per un caso d'uso simile per evitare potenziali problemi?

È stato utile?

Soluzione

Dopo ulteriori sperimentazioni: Sì, ci sono implicazioni nella cache che potrebbero portare a risultati incoerenti; NHibernate non può sapere automaticamente che la formula potrebbe cambiare i valori tra le query per i risultati dell'entità con lo stesso identificatore (e presume che non lo faccia).

Avere una mappatura di classe come quelle nella domanda comporterebbe la memorizzazione della classifica con il resto dei dati dell'entità. Ciò rende possibile che una query successiva finisca per restituire un valore di rango da qualche altra query piuttosto che la query in esecuzione e quindi avere rango che non sono sequenziali come previsto.

NHibernate ha query separate e cache di entità (lì sono in realtà due cache delle entità: la cache della sessione e la < a href = "http://nhforge.org/doc/nh/en/index.html#performance-cache" rel = "nofollow noreferrer"> cache entità di secondo livello ) e gli impatti dipendono da quali sono in uso.

Se la cache delle query non è abilitata, è possibile ricevere valori di classificazione errati due query diverse all'interno della stessa sessione che condividono un risultato ma con gradi diversi. In questo caso, la seconda query della stessa sessione non sovrascriverà i dati dell'entità già nella sessione dalla prima query (poiché potrebbe essere cambiata per quell'unità di lavoro), quindi il valore di rango restituito sarà lo stesso dalla prima query anziché il rango effettivo della seconda query. Eliminare i risultati dalla prima query dovrebbe evitare questo problema (ma non è la soluzione consigliata; vedi sotto)

Quando la cache di query è abilitata, è possibile ricevere valori di classificazione errati anche quando si ripete la stessa query dopo l'esecuzione di un'altra query che ha avuto un risultato con una classificazione diversa. In questo caso, la prima esecuzione della query aggiunge gli identificatori dei risultati alla cache delle query e le entità (con il loro grado) alla cache delle entità. La query interlacciata (se eseguita in un'altra sessione) potrebbe comportare una modifica al valore di classificazione memorizzato con l'entità nella cache dell'entità. Quando la prima query viene rieseguita, gli identificativi memorizzati nella cache vengono utilizzati per cercare le entità memorizzate nella cache (con i ranghi modificati).


Il problema può essere risolto completamente modificando l'entità in modo da includere solo i valori persistenti per l'entità (ovvero escludendo il rango). Quindi, per la query, utilizzare una proiezione per estrarre l'identificatore e il rango per quella query:

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.SetProjection
    (Projections.Id(), 
     Projections.SqlProjection("row_number() over(order by value) as Rank",
                               new[] { "Rank" },
                               new[] { NHibernateUtil.Int32 }));

In questo caso, poiché la classifica è un tipo di valore, la cache delle query memorizzerà la classifica accanto agli identificatori dei risultati della query per quella query specifica . Quindi, utilizzando una seconda query, cercare i valori dell'entità utilizzando gli identificatori proiettati. La parte difficile è che dovresti evitare un problema di tipo N + 1 quando esegui la query dell'entità e dovrai creare un'altra struttura di dati per sposare Entity e il suo grado associato per quella query.

È un po 'fastidioso dover utilizzare due query separate anziché una singola query, ma questo sembra essere l'unico modo per utilizzare le cache in modo appropriato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top