Hello,
I have some major issues with Hibernate Search 5.6.1.Final (Elasticsearch backend). I have a main entity that I index in Elasticsearch which has an @IndexedEmbedded ManyToOne association. If the embedded parent object has many (40K) child records, and it gets updated, I get this for the duration of 6 hours (!):
- CPU jumps to 80%
- the modification to the related object is blocked (eventually succeeds after 6h)
- any other modifications to that object are also blocked and time out with
Code:
org.hibernate.exception.LockTimeoutException: could not execute batch
caused by
java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
In the end I see that in my Elasticsearch cluster the number of deleted (reindexed) documents jumps by 40K.
Here is the simplified parent:
Code:
public class User {
...
@OneToMany(mappedBy = "user", targetEntity = Case.class, fetch = FetchType.LAZY)
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "entity.User.cases")
@ContainedIn
private Set<Case> cases;
}
Here is the simplified child:
Code:
@Indexed
@ClassBridge(impl = ComputedCaseFieldsClassBridge.class, analyzer = @Analyzer(definition = "custom_standard_analyzer"))
public class Case {
...
@ManyToOne(targetEntity = User.class, optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "FK5A0EB2FF8AFB978"))
@IndexedEmbedded(includeEmbeddedObjectId = true, prefix = "owner.", includePaths = {"user_id", "user_name", "user_email", "user_nickname"})
private User user;
}
It is worth mentioning that the Case entity has a ClassBridge that:
1. Searches for a Hibernate session in a ThreadLocal variable, if not there it opens it and sets it in the Threadlocal (tries to reuse a session across re-index jobs)
2. Executes a query to retrieve aggregated data to be included in the document - making joins to other tables
3. Enriches the Case document with additional fields
Because of the ClassBridge I believe dirty-checking is disabled for any modifications to a Case object or one of its embedded objects. Could it be that this bridge is simply too heavy which causes the slowness? I did not see excessive opening of database connections, so I believe sessions are being reused across indexing threads.
A thread dump during indexing reveals a single thread related to indexing:
Code:
"http-apr-443-exec-337" - Thread t@4804
java.lang.Thread.State: RUNNABLE
at org.hibernate.engine.internal.Collections.processReachableCollection(Collections.java:164)
at org.hibernate.event.internal.FlushVisitor.processCollection(FlushVisitor.java:42)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:104)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:65)
at org.hibernate.event.internal.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:59)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:155)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:44)
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1264)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1975)
at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:322)
at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125)
at org.hibernate.internal.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:966)
at x.x.x.x.x.CaseFacade.getAggregateDocumentDataOfCase(CaseFacade.java:499)
at x.x.x.x.x.ComputedCaseFieldsClassBridge.set(ComputedCaseFieldsClassBridge.java:76)
at org.hibernate.search.bridge.util.impl.ContextualExceptionBridgeHelper$OneWayConversionContextImpl.set(ContextualExceptionBridgeHelper.java:134)
at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.buildDocumentFieldForClassBridges(DocumentBuilderIndexedEntity.java:890)
at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.buildDocumentFields(DocumentBuilderIndexedEntity.java:454)
at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.getDocument(DocumentBuilderIndexedEntity.java:391)
at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.createUpdateWork(DocumentBuilderIndexedEntity.java:301)
at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.addWorkToQueue(DocumentBuilderIndexedEntity.java:243)
at org.hibernate.search.engine.impl.WorkPlan$PerEntityWork.enqueueLuceneWork(WorkPlan.java:520)
at org.hibernate.search.engine.impl.WorkPlan$PerClassWork.enqueueLuceneWork(WorkPlan.java:282)
at org.hibernate.search.engine.impl.WorkPlan.getPlannedLuceneWork(WorkPlan.java:154)
at org.hibernate.search.backend.impl.WorkQueue.prepareWorkPlan(WorkQueue.java:114)
at org.hibernate.search.backend.impl.BatchedQueueingProcessor.prepareWorks(BatchedQueueingProcessor.java:57)
at org.hibernate.search.backend.impl.PostTransactionWorkQueueSynchronization.beforeCompletion(PostTransactionWorkQueueSynchronization.java:66)
at org.hibernate.search.backend.impl.EventSourceTransactionContext$DelegateToSynchronizationOnBeforeTx.doBeforeTransactionCompletion(EventSourceTransactionContext.java:169)
at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:928)
at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:503)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2353)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2517)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2506)
- locked <6a2b09f4> (a org.apache.tomcat.util.net.AprEndpoint$AprSocketWrapper)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- locked <354c23a> (a java.util.concurrent.ThreadPoolExecutor$Worker)
I did not find a way to increase the number of batch indexing worker threads - is there a way?
Relevant Hibernate Search configuration:
Code:
"hibernate.search.default.worker.execution": "async"
"hibernate.search.default.index_flush_interval": "1000"
"hibernate.search.default.elasticsearch.max_total_connection": "30"
"hibernate.search.default.elasticsearch.max_total_connection_per_route": "10"
"hibernate.search.default.elasticsearch.read_timeout": "60000"
"hibernate.search.default.elasticsearch.index_management_wait_timeout": "30000"
I want to know if it is normal for a long-lasting batch indexing to block modification to records like that. Can I make it asynchronous and if yes - what are the implications? Modification to any record is mission-critical and I do not want Hibernate Search to interfere with it.
Thanks in advance!
Regards,
Ivan Krumov