Impacto en el almacenamiento en caché de NHibernate para búsquedas con resultados que incluyen el valor calculado asignado como una fórmula (por ejemplo, rango)

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

Pregunta

Al definir una propiedad calculada usando una fórmula en NHibernate, ¿cuáles son las implicaciones para cuando la fórmula varía su resultado dependiendo de las restricciones de la consulta, especialmente con respecto al almacenamiento en caché de consultas?

Más específicamente, considere la siguiente clase simple de 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; }
}

Mapeado con el siguiente mapeo simple de 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>

Ejecución con una fábrica de sesiones con la opción hibernate.cache.use_query_cache establecida en true y consultada de las siguientes maneras:

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 se comportará de manera razonable para las Entidades devueltas? O podría el " Rango " valor de una consulta en caché contaminar el valor de rango de otra consulta debido a la caché de consulta? ¿Hay alguna otra preocupación al usar una fórmula de este tipo en las asignaciones de NHibernate?

EDITAR:

También podría valer la pena señalar que, en mi caso particular, "Entidad". no es una entidad comercial de primera clase, sino una especie de metaentidad. Se asigna a una vista de base de datos indexada sobre otras entidades de primera clase y se usa exclusivamente para la búsqueda (la sesión. La llamada de carga (id) está ideada y nunca debería suceder en la práctica).

Y, si hay implicaciones para el almacenamiento en caché, como sospecho, ¿qué alternativas podrían existir para un caso de uso similar para evitar posibles problemas?

¿Fue útil?

Solución

Después de más experimentación: Sí, hay implicaciones de caché que podrían dar lugar a resultados inconsistentes; NHibernate no puede saber automáticamente que la fórmula podría cambiar los valores entre consultas para resultados de entidad con el mismo identificador (y supone que no lo hará).

Tener un mapeo de clase como los de la pregunta daría como resultado que el rango se almacene con el resto de los datos de la entidad. Esto hace posible que una consulta posterior termine devolviendo un valor de rango de alguna otra consulta en lugar de la consulta que se está ejecutando y, por lo tanto, tenga rangos que no son secuenciales como se esperaba.

NHibernate tiene consulta y cachés de entidades (allí) en realidad son cachés de dos entidades: el caché de sesión y el < a href = "http://nhforge.org/doc/nh/en/index.html#performance-cache" rel = "nofollow noreferrer"> caché de entidades de segundo nivel ) y los impactos dependen de cuáles son siendo utilizado.

Cuando el caché de consultas no está habilitado, se pueden recibir valores de clasificación incorrectos si realiza dos consultas diferentes dentro de la misma sesión que comparten un resultado pero con diferentes rangos. En este caso, la segunda consulta de la misma sesión no anulará los datos de la entidad que ya están en la sesión desde la primera consulta (ya que podría haber cambiado para esa unidad de trabajo), por lo que el valor de rango devuelto será el mismo desde la primera consulta en lugar del rango real de la segunda consulta. Expulsar los resultados de la primera consulta debería evitar este problema (pero no es la solución recomendada; ver más abajo)

Cuando el caché de consultas está habilitado, también se pueden recibir valores de rango incorrectos cuando se repite la misma consulta después de que se haya ejecutado alguna otra consulta que tuvo un resultado con un rango diferente. En este caso, la ejecución de la primera consulta agrega los identificadores de resultados al caché de consultas y las entidades (con su rango) al caché de entidades. La consulta intercalada (cuando se ejecuta en otra sesión) podría resultar en un cambio en el valor de rango almacenado con la entidad en el caché de la entidad. Cuando se vuelve a ejecutar la primera consulta, los identificadores en caché se usan para buscar las entidades en caché (con los rangos cambiados).


El problema puede abordarse por completo cambiando la entidad para incluir solo los valores persistentes de la entidad (es decir, excluyendo el rango). Luego, para la consulta, use una proyección para extraer el identificador y el rango de esa consulta:

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 }));

En este caso, dado que el rango es un tipo de valor, el caché de consultas almacenará el rango junto con los identificadores de resultados de la consulta para esa consulta específica . Luego, usando una segunda consulta, busque los valores de la entidad usando los identificadores proyectados. La parte difícil es que querrá evitar un problema de tipo N + 1 al realizar la consulta de entidad y deberá crear otra estructura de datos para casarse con la Entity y su rango asociado para esa consulta.

Es un poco molesto que tenga que usar dos consultas separadas en lugar de una sola consulta, pero esta parece ser la única forma de usar las cachés de manera adecuada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top