# Synchronization with the indexes : refresh problem

**URL:** <https://discourse.hibernate.org/t/synchronization-with-the-indexes-refresh-problem/6381>\
**Category:** Hibernate Search\
**Created:** [June 10, 2022, 1:59pm UTC](https://discourse.hibernate.org/t/synchronization-with-the-indexes-refresh-problem/6381 "2022-06-10T13:59:01Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![fraf](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/fraf/32/1614_2.png) [@fraf](https://discourse.hibernate.org/u/fraf)\
**Post date:** [June 10, 2022, 1:59pm UTC](https://discourse.hibernate.org/t/synchronization-with-the-indexes-refresh-problem/6381/1 "2022-06-10T13:59:01Z")

</div>

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 :

1. The user do a search query with multiple criteria.
2. The backend, through hibernate search, compute the results and return them to the view.
3. The view result is displayed.

This normal scenario is ok. But another scenario is causing refresh problem :

1. From 3), the user click on an action button in a single row result.
2. The backend process the action, then the view refreshes itself by calling again the backend to compute the new search result.
3. 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 set `hibernate.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 :

```auto
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 :

```auto
@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 :

```auto
@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

```auto
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.

---

<div class="post-metadata">

**Author:** ![yrodiere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yrodiere/32/1760_2.png) [@yrodiere](https://discourse.hibernate.org/u/yrodiere)\
**Post date:** [June 13, 2022, 6:36am UTC](https://discourse.hibernate.org/t/synchronization-with-the-indexes-refresh-problem/6381/2 "2022-06-13T06:36:38Z")

</div>

Hello,

> [@fraf](#):
>
> 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)

Sounds good, but you don’t really need to reset to defaults after commit: the strategy is set on the session only, and the session in your case will most likely be bound to the transaction, and will be closed on commit. So, you just don’t need to clean up anything: other sessions won’t be affected by your strategy change, and the only affected session won’t be used anymore after commit.

> [@fraf](#):
>
> 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

That’s probably your problem right there: I suspect your creating multiple transactions leads to multiple sessions being used for a single call, and they don’t share the same sync strategy. I’m not 100% sure, but since this transaction stuff looks complicated and (as explained above) is unnecessary, I would get rid of it.

What you need to do is:

- Simplify your aspect to only call `readSyncSynchronizationStrategy`, before the method being intercepted. Remove all transaction-related tricks.
- Make sure your aspect is applied inside the transaction interceptor. E.g. annotate the method with both `@Transactional` and `@IndexerReadSync` and somehow tell Spring that the transaction aspect should be applied before yours. A quick search on Google tells me annotating your aspect with `@Order(1)` should do the trick, or alternatively implementing `Ordered`.

Or just, you know… Call `searchSession.automaticIndexingSynchronizationStrategy` directly from the method, and don’t use custom annotations / aspects if they’re making your life harder 🙂

* * *

As a side note, you probably want to use `AutomaticIndexingSynchronizationStrategy.sync()` rather than `readSync()`, so that you get a refresh _and_ a commit. Technically they are probably equivalent right now with the Elasticsearch backend, since it always “commits” (kind of) and doesn’t provide an option for Hibernate Search to skip that, but that might not stay that way forever. (Also, `read-sync` is _already_ skipping commits with the Lucene backend).

---

<div class="post-metadata">

**Author:** ![fraf](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/fraf/32/1614_2.png) [@fraf](https://discourse.hibernate.org/u/fraf)\
**Post date:** [June 13, 2022, 8:49am UTC](https://discourse.hibernate.org/t/synchronization-with-the-indexes-refresh-problem/6381/3 "2022-06-13T08:49:36Z")

</div>

> [@yrodiere](#):
>
> Sounds good, but you don’t really need to reset to defaults after commit: the strategy is set on the session only, and the session in your case will most likely be bound to the transaction, and will be closed on commit. So, you just don’t need to clean up anything: other sessions won’t be affected by your strategy change, and the only affected session won’t be used anymore after commit.

So it simplifies all the aspect’s implementation if strategy is bound to the session, indeed.  
Then I did the final code according to your advice. The final scheme look like this :

Only the two added annotations :

```auto
@IndexerSyncStrategy
	@Transactional
	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;
	}

```

The Aspect :

```auto
@Aspect
@Order(Ordered.LOWEST_PRECEDENCE)
@Component
public class IndexationSynchronizationStrategy {

    private final SessionFactory sessionFactory;

    public IndexationSynchronizationStrategy(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Before(value = "@annotation(com.fiducial.signature.annotation.IndexerSyncStrategy) && @annotation(javax.transaction.Transactional)")
    public void syncStrategy(JoinPoint jp) throws Throwable {
        SearchSession searchSession = Search.session((EntityManager) sessionFactory.getCurrentSession());
        searchSession.automaticIndexingSynchronizationStrategy(AutomaticIndexingSynchronizationStrategy.sync());
    }
}

```

The order of precedence :

```auto
@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE - 1)
public class HibernateConfiguration extends AbstractHibernateConfiguration {

```

By the way, @Transactional has the lowest precedence by default.  
I did some test without setting any order on the 2 aspects (@Transactional and @IndexerSyncStrategy) and seems to work ; but I imagine without forces the order, the runtime order might be unpredictable.

> [@yrodiere](#):
>
> Or just, you know… Call `searchSession.automaticIndexingSynchronizationStrategy` directly from the method, and don’t use custom annotations / aspects if they’re making your life harder

You’re right but, since the application is multi-module, dependency is inevitable. The aspect is less code intrusive than a helper method (in addition it includes the dependency of sessionFactory)

Thank you.
