2016-06-21 10 views
0

У нас есть приложение, которое работает в кластере EC2 (в настоящее время для тестирования 2 узла). Для поиска доменных моделей мы используем Hibernate Search, и поскольку приложение работает в кластере, мы используем Infinispan как каталог Lucene. Чтобы пережить перезагрузку, мы используем хранилище кеша JDBC в MySQL, оба узла получают доступ к тем же таблицам MySQL. Чтобы учесть добавление и удаление узлов, мы используем бэкэнд «jgroups» для конфигурации рабочего сервера Hibernate.Повторяющиеся ошибки записи в Hibernate Search, поддерживаемые Infinispan в кластере EC2

Наша проблема заключается в дублировании записей, которые мы получаем, когда пытаемся восстановить весь индекс сущности. Мы получаем ошибки с аналогичным stacktraces, как это:

ERROR [AsyncStoreProcessor-LuceneIndexesData-0] [2016-06-21 17:01:59] org.infinispan.persistence.jdbc.stringbased.JdbcStringBasedStore - ISPN008024: Error while storing string key to database; key: '_d.fdt|0|1048576|com.model.SomeModel' 
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '_d.fdt|0|1048576|com.model.SomeModel' for key 'PRIMARY' 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041) 
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4190) 
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4122) 
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570) 
at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:1399) 
at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:857) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2460) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2377) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2361) 
at com.zaxxer.hikari.proxy.PreparedStatementJavassistProxy.executeUpdate(PreparedStatementJavassistProxy.java) 
at org.infinispan.persistence.jdbc.stringbased.JdbcStringBasedStore.write(JdbcStringBasedStore.java:174) 
at org.infinispan.persistence.async.AsyncCacheWriter.applyModificationsSync(AsyncCacheWriter.java:158) 
at org.infinispan.persistence.async.AsyncCacheWriter$AsyncStoreProcessor.retryWork(AsyncCacheWriter.java:330) 
at org.infinispan.persistence.async.AsyncCacheWriter$AsyncStoreProcessor.run(AsyncCacheWriter.java:312) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
at java.lang.Thread.run(Thread.java:745) 

Когда мы проверяем базу данных, есть запись с этим идентификатором. Когда мы пробуем его с одним узлом, ошибок нет. Поэтому мы предполагаем, что оба узла пытаются записать запись кэша в БД. Что может вызвать эту проблему? AFAIK для бэкэнда jgroups следует предотвратить.

Мы используем спящий режим 4.3.9.Финал, спящий поиск 5.2.1. Окончательный, бесконечный 7.2.5. Финальный и jgroups 3.6.8. Infinispan конфигурация выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?> 
<infinispan xmlns="urn:infinispan:config:7.2" 
      xmlns:jdbc="urn:infinispan:config:store:jdbc:7.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation=" 
      urn:infinispan:config:7.2 http://www.infinispan.org/schemas/infinispan-config-7.2.xsd 
      urn:infinispan:config:store:jdbc:7.2 http://www.infinispan.org/schemas/infinispan-cachestore-jdbc-config-7.2.xsd"> 

    <jgroups> 
     <stack-file name="tcp" path="default-configs/default-jgroups-tcp.xml"/> 
     <stack-file name="ec2" path="search/infinispan-jgroups-ec2.xml"/> 
    </jgroups> 

    <cache-container name="HibernateSearch" default-cache="default" statistics="false" shutdown-hook="DONT_REGISTER"> 

     <transport stack="${infinispan.transport:tcp}"/> 

     <!-- Duplicate domains are allowed so that multiple deployments with default configuration 
      of Hibernate Search applications work - if possible it would be better to use JNDI to share 
      the CacheManager across applications --> 
     <jmx duplicate-domains="true"/> 

     <!-- *************************************** --> 
     <!-- Cache to store Lucene's file metadata --> 
     <!-- *************************************** --> 
     <replicated-cache name="LuceneIndexesMetadata" mode="SYNC" remote-timeout="25000"> 
      <transaction mode="NONE"/> 
      <state-transfer enabled="true" timeout="480000" await-initial-transfer="true"/> 
      <indexing index="NONE"/> 
      <locking striping="false" acquire-timeout="10000" concurrency-level="500" write-skew="false"/> 
      <eviction max-entries="-1" strategy="NONE"/> 
      <expiration max-idle="-1"/> 
      <persistence passivation="false"> 
       <jdbc:string-keyed-jdbc-store preload="true" fetch-state="true" read-only="false" purge="false"> 
        <jdbc:data-source jndi-url="java:comp/env/jdbc/..."/> 
        <jdbc:string-keyed-table drop-on-exit="false" create-on-start="true" prefix="ISPN_STRING_TABLE"> 
         <jdbc:id-column name="ID" type="VARCHAR(255)"/> 
         <jdbc:data-column name="METADATA" type="BLOB"/> 
         <jdbc:timestamp-column name="TIMESTAMP" type="BIGINT"/> 
        </jdbc:string-keyed-table> 
        <property name="key2StringMapper">org.infinispan.lucene.LuceneKey2StringMapper</property> 
        <write-behind/> 
       </jdbc:string-keyed-jdbc-store> 
      </persistence> 
     </replicated-cache> 

     <!-- **************************** --> 
     <!-- Cache to store Lucene data --> 
     <!-- **************************** --> 
     <distributed-cache name="LuceneIndexesData" mode="SYNC" remote-timeout="25000"> 
      <transaction mode="NONE"/> 
      <state-transfer enabled="true" timeout="480000" await-initial-transfer="true"/> 
      <indexing index="NONE"/> 
      <locking striping="false" acquire-timeout="10000" concurrency-level="500" write-skew="false"/> 
      <eviction max-entries="-1" strategy="NONE"/> 
      <expiration max-idle="-1"/> 
      <persistence passivation="false"> 
       <jdbc:string-keyed-jdbc-store preload="true" fetch-state="true" read-only="false" purge="false"> 
        <jdbc:data-source jndi-url="java:comp/env/jdbc/..."/> 
        <jdbc:string-keyed-table drop-on-exit="false" create-on-start="true" prefix="ISPN_STRING_TABLE"> 
         <jdbc:id-column name="ID" type="VARCHAR(255)"/> 
         <jdbc:data-column name="DATA" type="MEDIUMBLOB"/> 
         <jdbc:timestamp-column name="TIMESTAMP" type="BIGINT"/> 
        </jdbc:string-keyed-table> 
        <property name="key2StringMapper">org.infinispan.lucene.LuceneKey2StringMapper</property> 
        <write-behind/> 
       </jdbc:string-keyed-jdbc-store> 
      </persistence> 
     </distributed-cache> 

     <!-- ***************************** --> 
     <!-- Cache to store Lucene locks --> 
     <!-- ***************************** --> 
     <replicated-cache name="LuceneIndexesLocking" mode="SYNC" remote-timeout="25000"> 
      <transaction mode="NONE"/> 
      <state-transfer enabled="true" timeout="480000" await-initial-transfer="true"/> 
      <indexing index="NONE"/> 
      <locking striping="false" acquire-timeout="10000" concurrency-level="500" write-skew="false"/> 
      <eviction max-entries="-1" strategy="NONE"/> 
      <expiration max-idle="-1"/> 
     </replicated-cache> 
    </cache-container> 

</infinispan> 

Соответствующая конфигурация Hibernate выглядит следующим образом (что делается с помощью Spring):

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="dataSource" ref="dataSource"/> 
    <property name="persistenceUnitName" value="..."/> 
    <property name="packagesToScan" value="com...."/> 
    <property name="jpaVendorAdapter"> 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
      <property name="generateDdl" value="false"/> 
      <property name="showSql" value="false"/> 
      <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/> 
      <property name="database" value="MYSQL"/> 
     </bean> 
    </property> 
    <property name="jpaPropertyMap"> 
     <map> 
      <entry key="hibernate.default_batch_fetch_size" value="50"/> 
      <entry key="hibernate.multiTenancy" value="SCHEMA"/> 
      <entry key="hibernate.multi_tenant_connection_provider" value-ref="connectionProvider"/> 
      <entry key="hibernate.tenant_identifier_resolver" value-ref="tenantIdentifierResolver"/> 
      <entry key="hibernate.cache.use_second_level_cache" value="true"/> 
      <entry key="hibernate.cache.region.factory_class" value="com.hazelcast.hibernate.HazelcastCacheRegionFactory"/> 
      <entry key="hibernate.cache.hazelcast.use_native_client" value="true"/> 
      <entry key="hibernate.cache.hazelcast.native_client_address" value="127.0.0.1"/> 
      <entry key="hibernate.cache.hazelcast.native_client_group" value="dev"/> 
      <entry key="hibernate.cache.hazelcast.native_client_password" value="dev-pass"/> 
      <entry key="hibernate.connection.characterEncoding" value="UTF-8"/> 
      <entry key="hibernate.connection.useUnicode" value="true"/> 
      <entry key="hibernate.search.default.directory_provider" value="infinispan"/> 
      <entry key="hibernate.search.default.locking_cachename" value="LuceneIndexesLocking"/> 
      <entry key="hibernate.search.default.data_cachename" value="LuceneIndexesData"/> 
      <entry key="hibernate.search.default.metadata_cachename" value="LuceneIndexesMetadata"/> 
      <entry key="hibernate.search.default.chunk_size" value="1048576"/> 
      <entry key="hibernate.search.infinispan.configuration_resourcename" value="search/hibernatesearch-infinispan.xml"/> 
      <entry key="hibernate.search.default.worker.backend" value="jgroups"/> 
      <entry key="hibernate.search.services.jgroups.configurationFile" value="search/infinispan-jgroups-ec2.xml"/> 
     </map> 
    </property> 
</bean> 
+0

Кажется немного странным использовать как каталог Infinispan (который позволяет всем узлам получить доступ к индексу), так и бэкэнд JGroups, который выполняет репликацию во второй раз. –

+0

Это то, что рекомендует Hibernate Search. Он должен гарантировать, что только один узел пытается записать в индекс. В противном случае узлы пытаются получить блокировки и продолжать ждать друг друга, когда они пытаются записать в один и тот же индекс. – berserk81

+0

Также не использует бэкэнд JGroups ничего не меняет, все еще повторяются записи, когда запрашивается полная перестройка индекса. – berserk81

ответ

2

Вы правильно на цели JGroups Backend и вашей конфигурации Hibernate выглядит правильно.

Проблема заключается в конфигурации компонента в Infinispan хранилища кэша, это есть «общий» атрибут, у которого по умолчанию является ложным .

<jdbc:string-keyed-jdbc-store 
    preload="true" 
    fetch-state="true" 
    read-only="false" 
    purge="false" 
    shared="true" <!-- FIX 

Как вы подозревали, каждый Infinispan узел собирается переписать ту же запись на «каждый» экземпляра хранилища кэша, так как он не догадывался, что каждый из ваших узлов фактически подключения к одной и той же базе данных.

Настройка по общий атрибут истинной должен гарантировать, что ядро ​​Infinispan будет координировать между узлами, так что один (и только один) узел запишет запись.

+0

Добавление shared = "true" устраняло проблему. Большое спасибо за решение. – berserk81

+0

отлично, спасибо, что подтвердили это! – Sanne