I have a question related to coroutines and KMongo. I'm using KMongo CoroutineCollection to find some records in the database. On my local computer it takes <0.5sec but when I do the same thing on development environment it takes 10-25 seconds.
The app runs on Spring Boot Netty server with reactor to support coroutines. I'm using also DGS to set up GraphQL endpoints. The simplified callstack is:
DGSQuery suspend fun -> suspend fun |
| -> suspend fun KMongo first CoroutineCollection.find()
| -> suspend fun KMongo second CoroutineCollection.find()
It seems the default batchSize is 2, so I can see multiple queries run under the hood.
My suspicion is that the code operates on 1 thread that is used across the app and that's why it takes much longer on dev vs local (less clients and scheduled operations).
This is how logs look like:
[ctor-http-nio-1][][] c.x.d.a.wellreport.***Resolver : wellReports run
[ctor-http-nio-1][][] c.x.d.d.wellreport.***Service : all***Provider.invoke() start
[ctor-http-nio-1][][] d.m.w.Mongo***Repository : fetchAll started
[tter-2-thread-1][][] org.mongodb.driver.protocol.command : Sending command '{"find": "***", "filter": {}, "batchSize": 100, "$db": "***", "lsid": {"id": {"$binary": {"base64": "hdGYjs3YSX6K5y2YJzUJkw==", "subType": "04"}}}}' with request id 56 to database *** on connection [connectionId{localValue:22, serverValue:1902}] to server localhost:27017
[ntLoopGroup-3-6][][] org.mongodb.driver.protocol.command : Execution of command with request id 56 completed successfully in 13.32 ms on connection [connectionId{localValue:22, serverValue:1902}] to server localhost:27017
[ntLoopGroup-3-6][][] org.mongodb.driver.operation : Received batch of 54 documents with cursorId 0 from server localhost:27017
[ntLoopGroup-3-6][][] d.m.w.Mongo***Repository : fetchAll finished
[ntLoopGroup-3-6][][] c.x.d.d.wellreport.***Service : all***Provider.invoke() finished
[ntLoopGroup-3-6][][] c.x.d.d.wellreport.***Service : positive***Provider.invoke() start
[ntLoopGroup-3-6][][] w.Mongo***Repository : fetchAllPositive start
[tter-2-thread-1][][] org.mongodb.driver.protocol.command : Sending command '{"find": "***", "filter": {"score": {"$gt": 0.0}}, "batchSize": 2, "$db": "***", "lsid": {"id": {"$binary": {"base64": "hdGYjs3YSX6K5y2YJzUJkw==", "subType": "04"}}}}' with request id 57 to database *** on connection [connectionId{localValue:22, serverValue:1902}] to server localhost:27017
[ntLoopGroup-3-6][][] org.mongodb.driver.protocol.command : Execution of command with request id 57 completed successfully in 5.26 ms on connection [connectionId{localValue:22, serverValue:1902}] to server localhost:27017
[ntLoopGroup-3-6][][] org.mongodb.driver.operation : Received batch of 2 documents with cursorId 6831622277590159417 from server localhost:27017
This is how repository class look like:
class MongoXXXScoreRepository(private val collection: CoroutineCollection<MongoXXXScore>) : XXXScoreRepository {
private val logger by logger()
override suspend fun fetchAllPositive(): List<XXXScore> {
logger.info("fetchAllPositive start")
val result = collection.find(MongoXXXScore::score gt 0.0).toList().map { it.toXXXScore() }
logger.info("fetchAllPositive start")
return result
}
}
Interesting part to me is that after calling the first KMongo .find() the thread changes to a different one and continues on that thread till the end. This doesn't sound right to me. Any thoughts?
I think multiple round-trip costs is a more likely explanation. If you have n documents, the time taken is n/2*rtt.
I don't think it's a stretch to imagine your dev environment has a round trip time 20x your local machine.
So an easy fix is to simply increase your
batchSize.This is less surprsing when you remember coroutines are pretty much callbacks.
Dispatchers.Default, for example, has a thread pool to schedule multiple coroutines. This is called M:N threading.