Skip to content

Transactional not working with grpc coroutine service #1142

@gbtrevisan

Description

@gbtrevisan

The context

I want to be able to use spring Transactional support (using annotation or transactionalOperator) in a method called from a GrpcService.

`

class GrpcServiceTransactionalTest : IntegrationTest() {

@GrpcClient("inProcess")
private lateinit var grpcStub: grpcStub

@Autowired
private lateinit var myService: MyService

// THIS FAILS WITH COROUTINE TIMEOUT
@Test
fun `calling from grpc stub`() = runTest {
    for (i in 1..100) {
        shouldNotThrowAny {
            grpcStub.create(
                createRequest()
            )
        }
    }
}

// THIS DOES NOT FAIL
@Test
fun `calling directly service`() = runTest {
    for (i in 1..100) {
        shouldNotThrowAny {
            myService.create(
                createRequest()
            )
        }
    }
}

}

`

The question

Right now I can't use transactional neither in the Grpc method or in any method it calls because I get myself in what it seems to be a deadlock. I want to know if this is a bug or what, because using the transactional annotation from spring without calling it from the GrpcService works fine.

Stacktraces and logs

Here is log of the coroutine context with the transaction context
2024-10-02T13:12:55.431-03:00 INFO 147887 --- [my-component] [2 @coroutine#11] c.s.c.b.m.d.a.MyService : [io.grpc.kotlin.GrpcContextElement@109247f7, Context2{class org.springframework.transaction.reactive.TransactionContextHolder=org.springframework.transaction.reactive.TransactionContextHolder@53bd2de5, class org.springframework.transaction.reactive.TransactionContext=org.springframework.transaction.reactive.TransactionContext@6a52073f}, CoroutineId(11), "coroutine#11":ScopeCoroutine{Active}@fe4db76, Dispatchers.Default]

Error:
When I run the test where I call the coroutine grpc service stub, during the execution the code stops for a moment, as if it was waiting for something (this is where I imagine that a deadlock happened within my transaction). After a while the execution failed because the test timed out waiting for the coroutine to complete.

Coroutine timeout:
After waiting for 1m, the test coroutine is not completing, there were active child jobs: [ScopeCoroutine{Active}@39d8f157] kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 1m, the test coroutine is not completing, there were active child jobs: [ScopeCoroutine{Active}@39d8f157] at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$2$1.invoke(TestBuilders.kt:351) at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$2$1.invoke(TestBuilders.kt:335) at kotlinx.coroutines.InternalCompletionHandler$UserSupplied.invoke(CompletionHandler.common.kt:67) at kotlinx.coroutines.InvokeOnCancelling.invoke(JobSupport.kt:1438) at kotlinx.coroutines.JobSupport.notifyCancelling(JobSupport.kt:1483) at kotlinx.coroutines.JobSupport.tryMakeCancelling(JobSupport.kt:806) at kotlinx.coroutines.JobSupport.makeCancelling(JobSupport.kt:766) at kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core(JobSupport.kt:682) at kotlinx.coroutines.JobSupport.cancelCoroutine(JobSupport.kt:669) at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:156) at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run(EventLoop.common.kt:498) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277) at kotlinx.coroutines.DefaultExecutor.run(DefaultExecutor.kt:105) at java.base/java.lang.Thread.run(Thread.java:1583) Suppressed: java.lang.AssertionError: No exception expected, but a CancellationException was thrown.

The application's environment

Which versions do you use?

  • Spring (boot): 3.3.2
  • grpc-kotlin-stub: 1.4.1
  • grpc-spring-boot-starter: 3.1.0.RELEASE
  • java: 21 64bit
  • kotlinVersion: 2.0.10

Additional information

  • Did it ever work before?
    It works if I don't call from the grpc service or stub

  • How can we reproduce it?
    Create a kotlin coroutine grpc service and create a service with a method annotated with @transactional from spring
    In the method annotated with @transactional you have to launch new coroutines and do some database operation, in my case I'm inserting in mysql using r2dbc driver and CoroutineCrudRepository.

  • Do you have a demo?
    No

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionA question about this library or its usage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions