Wumba
Wumba

Reputation: 107

Getting error after upgrading to spring boot 3 using DefaultLockRepository

I am currently upgrading legacy code from spring-boot v2.7.1 to spring-boot v3.1.0. Unfortunately, I never had direct contact with JDBC. I mostly use JPA.

The error I get is the following:

org.springframework.dao.CannotAcquireLockException: Failed to lock mutex at b02643f1-227e-4893-9260-cdbi18x6f377

...

Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.transaction.support.TransactionTemplate.execute(org.springframework.transaction.support.TransactionCallback)" because "this.serializableTransactionTemplate" is null
    at org.springframework.integration.jdbc.lock.DefaultLockRepository.acquire(DefaultLockRepository.java:343)
    at org.springframework.integration.jdbc.lock.JdbcLockRegistry$JdbcLock.doLock(JdbcLockRegistry.java:272)
    at org.springframework.integration.jdbc.lock.JdbcLockRegistry$JdbcLock.tryLock(JdbcLockRegistry.java:253)
    ... 73 more

JDBC was used in the following way:

  private JdbcLockRegistry getJdbcLockRegistry() {
        final var dataSource = getDatasource
        final var defaultLockRepository = new DefaultLockRepository(dataSource);
        defaultLockRepository.setPrefix(getSchema());
        defaultLockRepository.afterPropertiesSet();
        return new JdbcLockRegistry(defaultLockRepository);
    }

Now, the DefaultLockRepository class changed because of the upgrade:

After debugging, I concluded, that I have to use it the following way:

@Autowired
ApplicationContext context;

    private JdbcLockRegistry getJdbcLockRegistry() {
        final var dataSource = getDatasource();
        final var defaultLockRepository = new DefaultLockRepository(dataSource);
        defaultLockRepository.setApplicationContext(context);
        defaultLockRepository.setPrefix(getSchema());
        defaultLockRepository.afterSingletonsInstantiated();
        defaultLockRepository.afterPropertiesSet();
        return new JdbcLockRegistry(defaultLockRepository);
    }

Is this valid? Why Do I have to autowire the ApplicationContext myself? Normally, Spring handles injection of Beans and therefore the ApplicationContext itself? I am fairly confused by the changes. The tests are running tough.

Upvotes: 2

Views: 1389

Answers (1)

Mykhailo Hodovaniuk
Mykhailo Hodovaniuk

Reputation: 521

Probably this happens because there is code that instantiates some singleton bean that depends on JdbcLockRegistry and starts using it. The problem is that org.springframework.integration.jdbc.lock.DefaultLockRepository#afterSingletonsInstantiated Invoked right at the end of the singleton pre-instantiation phase, with a guarantee that all regular singleton beans have been created already.

For example, the next code will cause a similar error:

@Bean
public LockRegistryLeaderInitiator leaderInitiator(LockRegistry lockRegistry) {
  var lockRegistryLeaderInitiator = new LockRegistryLeaderInitiator(lockRegistry);
  lockRegistryLeaderInitiator.start();
  return lockRegistryLeaderInitiator;
}

To fix it we need to move lockRegistryLeaderInitiator.start() outside. One possible solution is to run it after the Spring context has been initialized:

@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
    lockRegistryLeaderInitiator.start();
}

Upvotes: 0

Related Questions