Skip to content

Inconsistent behaviour on transactional async method #32709

@karbi

Description

@karbi

Affects: 6.1.0+

Transactional method returning Future will sometimes be rollback, sometimes not, depending on time that Future is exceptionally completed. Here is example:

@Transactional public CompletableFuture<Void> asyncMethod() { doSomeDatabaseModification(); if (!isHealth()) { return CompletableFuture.failedFuture(new RuntimeException()); // case 1 } final CompletableFuture<Void> result = new CompletableFuture<>(); new Thread(() -> { try { Thread.sleep(10_000); } catch (InterruptedException e) { result.completeExceptionally(e); // case 2 return; } result.complete(null); }).start(); return result; }

In case 1 transaction will be rolled back, in case 2 transaction will be committed, despite in both cases CompletableFuture is completed with error. From that reason method user cannot make any assumptions about transaction state after failure.

For example it could lead to unintended behaviour in method methodDependingOnRollback():

@Transactional public void methodDependingOnRollback() { final CompletableFuture<Void> result = asyncMethod(); while (!result.isDone()); // ugly but only on purpose of example // assuming that in case of asyncMethod() failure, database will not be modified saveSuccessInDatabase(); }

It also breaks code that was working in Spring 5, like methodWorkingInSpring5():

@Transactional public void methodWorkingInSpring5() { asyncMethod().whenComplete((v, e) -> { if (e != null) { saveErrorReasonInDatabase(e); } }); }

Breaking change was introduced by issue #30018. Probably the same problem is with Vavr, but I'm not familiar with this library.

Metadata

Metadata

Assignees

Labels

in: dataIssues in data modules (jdbc, orm, oxm, tx)type: documentationA documentation task

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions