java - second - net sf ehcache hibernate
Muchas lecturas simultáneas+una escritura causa la excepción ObjectNotFoundException debido a ehcache (3)
Uso Hibernate 3.6.8, ehcache 2.4.5 (también probado con el último 2.8.0), jvm 1.6.0_22 en un sitio de alto tráfico, y algunas veces experimento
ObjectNotFoundException: no existe ninguna fila con el identificador dado: [com.example.Foo # 123] `
cuando se crea un nuevo Foo
(en este caso con id 123) a través del código más simple posible:
Foo foo = new Foo();
session.save(foo);
La razón es que en todas las páginas de este sitio de alto tráfico obtengo todos los Foo
s así:
session.createQuery("from Foo").setCacheable(true).list();
La tabla que almacena Foo
s contiene 1000 filas y la entidad se almacena en caché en ehcache:
<class-cache class="com.example.Foo" usage="read-write" />
Otras partes posiblemente relevantes de mi configuración de Hibernate son:
<property name="connection.url">jdbc:mysql://localhost:3306/example?characterEncoding=UTF-8</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">60</property>
<property name="hibernate.c3p0.min_size">10</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.max_statements">0</property>
<property name="hibernate.c3p0.timeout">0</property>
<property name="hibernate.c3p0.acquireRetryAttempts">1</property>
<property name="hibernate.c3p0.acquireRetryDelay">1</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.jdbc.use_scrollable_resultset">true</property>
<property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</property>
<property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>
<property name="hibernate.cache.use_query_cache">true</property>
El error ocurre una vez y luego desaparece. Sospecho que el caché de consultas de ehcache se actualiza con el nuevo id de entidad (123), pero el caché de la entidad aún no se ha actualizado con el contenido de esa entidad. Lo reproduzco con bastante facilidad localmente utilizando JMeter.
¿Alguna idea de cómo resolver esto?
En la creación de Foo
la ObjectNotFoundException
se lanza una vez. Si, por otro lado, elimino una instancia de Foo
entonces constantemente (y para siempre) obtengo la ObjectNotFoundException
para cada ejecución de .list()
. El stacktrace se puede ver en http://pastebin.com/raw.php?i=dp3HBgDB
La estrategia de read-write
no garantiza la transaccionalidad entre la base de datos y el caché, por lo que creo que esto es lo que sucede cuando se produce la escritura:
El nuevo objeto Foo se adjunta a la sesión de hibernación de la escritura.
la sesión de hibernación asociada a la solicitud de escritura inserta un proxy de carga diferida en el caché de segundo nivel.
El nuevo Foo se insertará en la base de datos en esa misma sesión, pero el inserto tardará un cierto tiempo en construirse, vaciarse y comprometerse.
Mientras tanto, otra solicitud como golpear el proxy para cargar todos Foos. Encuentra el proxy de carga diferida en la memoria caché (consulte stacktrace
DefaultLoadEventListener.proxyOrLoad()
) y decide cargar el objeto (DefaultLoadEventListener.load()
).Esto activa una
load()
Hibernateload()
de un Foo que el hilo de escritura no ha insertado todavía en la base de datos.No se encuentra ningún Foo con esa identificación en la base de datos, por
ObjectNotFoundException
se lanza unaObjectNotFoundException
.
Para confirmar esto, coloque un punto de interrupción de excepción en su IDE, para ver que en el momento en que se lanza la excepción, el objeto aún no se había insertado en la base de datos. Una forma de resolverlo sería utilizar la estrategia transactional .
Para mitigar el caso en el que se elimina una entidad y luego la list()
no funciona en absoluto, he detectado la ObjectNotFoundException
en un nivel superior y cuando esto sucede, lo hago:
session.getSessionFactory().getCache().evictCollectionRegions();
session.getSessionFactory().getCache().evictDefaultQueryRegion();
session.getSessionFactory().getCache().evictQueryRegions();
Al borrar el caché de segundo nivel, el sitio vuelve a funcionar. Por supuesto, esto no evita que el problema se produzca, pero resuelve el problema del tiempo de inactividad de todo el sitio.
Tomado de la documentación sobre la configuración de caché :
The following attributes and elements are optional.
timeToIdleSeconds:
Sets the time to idle for an element before it expires.
i.e. The maximum amount of time between accesses before an element expires
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that an Element can idle for infinity.
The default value is 0.
timeToLiveSeconds:
Sets the time to live for an element before it expires.
i.e. The maximum time between creation time and when an element expires.
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that and Element can live for infinity.
The default value is 0.
O también puedes ir con las opciones alternativas: