Подтвердить что ты не робот

Многие параллельные чтения + одна причина записи ObjectNotFoundException из-за ehcache

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

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

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

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

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

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

Сохранение таблицы Foo содержит 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(). Стекту можно увидеть на http://pastebin.com/raw.php?i=dp3HBgDB

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

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

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

Чтобы подтвердить это, поместите контрольную точку исключения в свою среду IDE, чтобы увидеть, что на данный момент исключение не было добавлено в базу данных. Одним из способов решения этой проблемы было бы использовать стратегию транзакционная.

Ответ 3

Взято из документации по Конфигурация кэша:

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.

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

Свежесть данных и срок действия