Hi searchers,
I’am confronted to a problem that operate on a search screen. A classic problem I think.
The use case is as follows :
- The user do a search query with multiple criteria.
- The backend, through hibernate search, compute the results and return them to the view.
- The view result is displayed.
This normal scenario is ok. But another scenario is causing refresh problem :
- From 3), the user click on an action button in a single row result.
- The backend process the action, then the view refreshes itself by calling again the backend to compute the new search result.
- The problem here, it that the action process impacts the search result. That is to say, the action do a kind of database deletion, and so, the automatic indexing is only triggered by polling interval for updating the index. But this time is “too long”, because the view has already called the backend which is doing the query already BEFORE indexes are updated. If I do a Thread.sleep(2000) before the query, then the new result has taken into account the deletion (1 result less than at 2)).
Another possibility to correct the sync problem is to sethibernate.search.automatic_indexing.synchronization.strategy = read-sync
instead of default
hibernate.search.automatic_indexing.synchronization.strategy = write sync
But I’am still not satisfied with this configuration because this use case is isolated, and I don’t want to impact the whole behavior of hibernate search only for this use case. Because, read-sync has Throughput to “Medium to worst”. Basically, we are losing in performance.
So I come to my work. I wish to change the behavior of hibernate.search.automatic_indexing.synchronization.strategy
programmatically with a method like :
private void readSyncSynchronizationStrategy() {
SearchSession searchSession = Search.session((EntityManager) sessionFactory.getCurrentSession());
searchSession.automaticIndexingSynchronizationStrategy(AutomaticIndexingSynchronizationStrategy.readSync());
}
The idea, is to turn the strategy to readSync BEFORE the transaction and turn back to writeSync (the default) AFTER the transaction commit (the commit trigger the automatic indexing)
Here is my code :
@Aspect
@Component
public class IndexationSynchronizationStrategy {
private final SessionFactory sessionFactory;
private final PlatformTransactionManager transactionManager;
public IndexationSynchronizationStrategy(SessionFactory sessionFactory, PlatformTransactionManager transactionManager) {
this.sessionFactory = sessionFactory;
this.transactionManager = transactionManager;
}
@Around(value = "@annotation(com.fiducial.signature.annotation.IndexerReadSync)")
public Object indexReadSync(ProceedingJoinPoint joinPoint) throws Throwable {
final boolean[] rollbackOuterTrx = {false};
final Object[] returnedObject = new Object[1];
final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
readSyncSynchronizationStrategy();
transactionTemplate.execute(new TransactionCallback<>() {
@Override
public String doInTransaction(TransactionStatus status) {
try {
returnedObject[0] = joinPoint.proceed();
return "done";
} catch (Throwable e) {
rollbackOuterTrx[0] = true;
status.setRollbackOnly();
return "failed";
}
}
});
defaultSynchronizationStrategy();
if (rollbackOuterTrx[0]) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return returnedObject[0];
}
private void readSyncSynchronizationStrategy() {
SearchSession searchSession = Search.session((EntityManager) sessionFactory.getCurrentSession());
searchSession.automaticIndexingSynchronizationStrategy(AutomaticIndexingSynchronizationStrategy.sync());
}
private void defaultSynchronizationStrategy() {
SearchSession searchSession = Search.session((EntityManager) sessionFactory.getCurrentSession());
searchSession.automaticIndexingSynchronizationStrategy(AutomaticIndexingSynchronizationStrategy.writeSync());
}
And the action method that causes the refresh problem :
@Service("gestionDepotBS")
public class GestionDepotBSImpl extends AllegoriaSecuredService implements GestionDepotBS {
@Override
@IndexerReadSync
public HttpStatus annulerDepotDac(Long id) {
checkIdNotNull(id);
Depot depot = depotDao.load(id);
if (depot == null) {
return HttpStatus.NOT_FOUND;
}
depot.setDateEtatRetour(new Date());
depot.modifierEtatDepot(ChoixEtatDepot.ANNULER_PAR_UTILISATEUR);
depot.setAnnule(Boolean.TRUE);
depotDao.update(depot);
return HttpStatus.OK;
}
}
through the annotation @IndexerReadSync
I trigger the switch/restore the strategy behavior, thanks to the aspect class IndexationSynchronizationStrategy
. That’s the idea.
The original method called with joinPoint.proceed();
is done in a inner transaction because the automatic indexing is only triggered with commit. I “merged” the inner transaction to the outer transaction with the code
if (rollbackOuterTrx[0]) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
because the orginal method is already called within a transaction (and I execute it in a inner transaction to provoke a commit statement)
But of course, all this to say that is doesn’t work more : at the screen, the refresh problem still occurs. The new result still contains the deleted single result. The sync strategy seems not working with my aspect ; which is well called (tested with breakpoint). Something is missing, but what ?
Thanks for your help.