Hi! I'm not too intimate with Coroutines, but I am...
# coroutines
a
Hi! I'm not too intimate with Coroutines, but I am trying write a few wrappers for JDBI that makes it play nicer with them. There are tons of obstacles to this, but first I want a test to provoke the problem to show other people... And to do that, I want to make sure two operations run on different threads. I need this because JDBI has a built in thread local (which I want to get rid of by showing the problem). But they end up executing on the same thread... Is my assumption right that the following code could execute on different threads in a system with more traffic? If so, how can I write a test that uses coroutines, but guarantees to run on different threads as to provoke the problem?
Copy code
@Test
    fun shouldExecuteOnDifferentThreads() {
        runBlocking {
            // Some blocking DB operation
            val firstThread = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
                Thread.currentThread()
            }

            // Another blocking DB operation
            val secondThread = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
                Thread.currentThread()
            }
            assertThat(firstThread).isNotSameAs(secondThread)
        }
    }
b
After the first withContext add a launch(Dispatchers.IO) { Thread.sleep(1000) } (To make it cleaner instead of sleep you can use smth like countdownlatch that waits for your second withContext to complete)
a
Hjm... Not sure I get the coundownlatch comment, but adding Thread.sleep(1000) didn't help. πŸ˜• They execute in sequence though, so I might need to saturate the dispatcher with other async tasks that have Thread.sleep in them?
Ah... Wrapping sleep like this worked in the first block:
Copy code
async { Thread.sleep(1000) }
Or not... Works at first... But then I needed two tests that does this... And they affect eachother. 😞
j
You could use custom dispatchers (each backed by one thread) and run the 2 operations in different coroutines on each dispatcher:
Copy code
@OptIn(DelicateCoroutinesApi::class)
@Test
fun shouldExecuteOnDifferentThreads() = runBlocking {
    val thread1 = newSingleThreadContext("thread-1")
    val thread2 = newSingleThreadContext("thread-2")

    // Some blocking DB operation
    val firstThread = withContext(thread1) {
        Thread.currentThread()
    }

    // Another blocking DB operation
    val secondThread = withContext(thread2) {
        Thread.currentThread()
    }
    assertThat(firstThread).isNotSameAs(secondThread)
}
s
@Anders Sveen Any luck writing some wrappers? πŸ™‚
a
Sorry @Srikanth Raju, I gave up πŸ™‚ I ended up creating a TransactionManager abstraction that is explicitly passed in instead. πŸ™‚
s
Does the abstraction manage launching jdbi transactions on separate threads?
a
We bypass the JDBI threadlocals by making sure we use jdbi.open() and not the other helpers that automatically uses ThreadLocals. Don't remember which ones exactly, but check the code of the ones you use. πŸ™‚ So we make sure the JDBI transaciton we are in are only used and handled locally. πŸ™‚
s
a
Yep. Many of the convenience methods in JDBI uses that so you get ThreadLocals without knowing. That can become a real issue once you get failing transactions/all or nothing semantics for co-routines that switch threads. Wish it were something that was easy to opt out of but kind of tightly integrated into the code. πŸ™‚
πŸ’€ 1