2014-01-23 2 views
18

Я использую Hibernate 3.6.8, ehcache 2.4.5 (также пробовал с последними 2,8.0), jvm 1.6.0_22 на сайте с высоким трафиком, а иногда я испытываюМногие одновременные чтения + одна причина записи ObjectNotFoundException из-за ehcache

ObjectNotFoundException: ни в одной строке с данным идентификатором не существует: [com.example.Foo # 123] `

, когда новый Foo (в данном случае с идентификатором 123) создается с помощью простейшего кода возможного :

Foo foo = new Foo(); 
session.save(foo); 

Причина заключается в том, что во всех страницах этого высокого трафику я получаю все Foo сек, как это:

session.createQuery("from Foo").setCacheable(true).list(); 

Таблица хранения Foo S содержит 1000 строк, и объект кэшируется в Ehcache:

<class-cache class="com.example.Foo" usage="read-write" /> 

Другие, возможно, соответствующие части моей конфигурации Hibernate являются:

<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> 

Ошибка происходит один раз, а затем исчезает. Я подозреваю, что кеш запросов ehcache обновляется с идентификатором id (123) объекта, но кеш-объект еще не обновлен содержимым этого объекта. Я воспроизвожу это довольно легко локально с помощью JMeter.

Любая идея о том, как это решить?

Foo создание ObjectNotFoundException бросается один раз. Если, с другой стороны, я удаляю экземпляр Foo, тогда я постоянно (и навсегда) получаю ObjectNotFoundException за каждое исполнение .list(). StackTrace можно увидеть на http://pastebin.com/raw.php?i=dp3HBgDB

+0

могли бы вы опубликовать свой файл ehcache.xml –

+0

@AshishJagtap: http://pastebin.com/raw.php?i=LdgWiLE0 – cherouvim

+0

И вы подтвердили, что исключение на самом деле выбрасывается при обращении к кэш-памяти, а не БД ? Возможно, косвенно через сквозной кеш? – Ralf

ответ

2

Чтобы уменьшить случай, когда удаляется сущность, а затем list() не работает вообще, когда я поймал ObjectNotFoundException на более высоком уровне, и когда это происходит, я делаю:

session.getSessionFactory().getCache().evictCollectionRegions(); 
session.getSessionFactory().getCache().evictDefaultQueryRegion(); 
session.getSessionFactory().getCache().evictQueryRegions(); 

Очистка кэша второго марок уровня сайт снова работает. Это, конечно, не мешает возникновению проблемы, но она решает проблему простоя всего сайта.

-1

Взятые из документации на Cache Configuration:

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. 

Или вы можете также пойти с альтернативными вариантами:

Data Freshness and Expiration

+0

Моя проблема заключается не в истечении срока действия кеша, а в том, что не все регионы кеша (кеш запросов и кэш объектов) быстро обновляются с учетом того, что что-то было помещено или удалено. – cherouvim

11

read-write стратегия не гарантирует транзакцию между базой данных и d кэш, так что я думаю, что это то, что происходит, когда происходит запись:

  • новый объект Foo прикреплен к спящей сессии записи.

  • ленивый загрузочный прокси вставлен в кеш второго уровня сеансом гибернации, связанным с запросом на запись.

  • Новый Foo будет вставлен в базу данных тем же самым сеансом, но вставка займет определенное время, чтобы быть построенным, покрасневшим и зафиксированным.

  • Между тем другой запрос, который попал в прокси, чтобы загрузить все Foos.Он находит ленивый прокси-сервер загрузки в кеше (см. Стекtrace DefaultLoadEventListener.proxyOrLoad()) и решает загрузить объект (DefaultLoadEventListener.load()).

  • Это вызывает Hibernate load() файла Foo, который еще не вставлен в базу данных по записи.

  • No Foo с этим идентификатором находится в базе данных, поэтому бросается ObjectNotFoundException.

Чтобы подтвердить это, положить exception breakpoint на вашем IDE, чтобы увидеть, что на данный момент исключение генерируется объект еще не был включен в БД. Один из способов его решения - использовать стратегию transactional.

+1

Я использовал «транзакционный» ehcache один раз в прошлом и быстро возвращался, потому что возникали всевозможные другие несоответствия. После некоторого расследования выяснилось, что причина в том, что мой уровень изоляции транзакций db - «REPEATABLE_READ», и для работы он должен был «READ_COMMITTED». – cherouvim

+0

Ваше объяснение имеет большой смысл. – cherouvim

+3

Объяснение кажется довольно возможным, у меня есть только одно возражение. Foo HAS был вставлен в db (и, вероятно, зафиксирован), так как у нас есть идентификатор. Однако кеш-объект еще не обновлен, в результате чего другой поток выполняет запрос select по id. Запрос ничего не возвращает, поскольку уровень изоляции db - REPEATABLE_READ. И из-за этого он не получит никакого результата, даже если другой поток действительно совершил вставку. –

Смежные вопросы