Buffered batched loading optimization with Kotlin Coroutines and Channels

Batching many API requests in a single API call 😅

Enough words! Let’s get to the implementation!

interface Loader<ID, T> {
suspend fun loadByIds(ids: Set<ID>): Map<ID, T>
}
private data class LoadRequest<ID, T>(  
val ids: Set<ID>,
val result: CompletableDeferred<Map<ID, T>>
)
val additionalRequest: LoadRequest<ID, T>? = 
select {
requests.onReceive { it }
onTimeout(0) { null }
}
try {
val loadedObjects = delegateLoader.loadByIds(idsToLoad)
requestsToProcess.forEach { processedRequests ->
processedRequests.result.complete(
loadedObjects.filterKeys { processedRequests.ids.contains(it) }
)
}
} catch (e: Exception) {
requestsToProcess.forEach { it.result.completeExceptionally(e) }
}

Testing

val requests = 3_000
(1..requests).map { step ->
val idToLoad = step % 300
async {
val result = loader.loadByIds(setOf(idToLoad))
assertEquals(1, result.size)
assertEquals(idToLoad, result[idToLoad])
}
}
.awaitAll()
val regularTime = measureTime { loadTest(TestLoader()) }
val batchedTime = measureTime {
loadTest(BatchLoader(delegateLoader = TestLoader()))
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store