Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where is the "No transaction in context" exception coming from?

I am using the reactive MongoDb driver by adding a spring-boot-starter-data-mongodb-reactive dependency to my Spring Boot project. This error has appeared after upgrading to Spring Boot 2.2.x.

It turns out, that if I do something as simple as:

class Something(@Id val name: String)

@Repository
interface SomethingRepository: ReactiveCrudRepository<Something, String>

@SpringBootTest
class DemoApplicationTests
{
    @Autowired protected lateinit var repository: SomethingRepository

    @Test
    fun test()
    {
        repository
            .save( Something("1") )
            .onErrorContinue { throwable, _ -> println(throwable.message) }
            .block()
    }
}

I get the output:

...
2019-12-12 20:58:48.379  INFO 24425 --- [    Test worker] com.example.demo.DemoApplicationTests    : Started DemoApplicationTests in 2.545 seconds (JVM running for 3.987)
No transaction in context
No transaction in context
...

It seems very odd, for a couple of reasons:

  1. The object gets successfully saved into the database.
  2. The error is reported twice, even though the pipe is composed of a Mono
  3. The documentation of onErrorContinue suggests that the operator drops the element which causes an error, which is in conflict with 1.
  4. The error is caused by a NoTransactionInContextException which is part of the springframework.transaction, but I am not even doing anything with transactions.

Has anybody experienced this problem? Is this a bug in the framework, or am I doing something wrong?

like image 824
Martin Drozdik Avatar asked Dec 12 '19 20:12

Martin Drozdik


Video Answer


1 Answers

onErrorContinue puts OnNextFailureStrategy.ResumeStrategy into subscriber context. This strategy ignores element.

Spring puts Transaction into subscriber context when method is transactional. Then Spring tries to find transaction in context and throws exception if transaction is not present. By default Spring ignores this exception in non-transactional methods and continues execution. But in case of onErrorContinue OnNextFailureStrategy.ResumeStrategy takes control and ignores element.

You can try this :

Flux.just(something1, something2)
    .flatMap(this::save)
    .subscribe();

...

private Mono<Something> save(Something s) {
        return Mono.just(s)
                   .flatMap(somethingRepository::save)
                   .onErrorResume(t -> {
                       log.error("Failed to save : {}", s, t);
                       return Mono.empty();
                   });
    }
like image 115
Olegs Sedacs Avatar answered Oct 25 '22 19:10

Olegs Sedacs