kotlinkotlin-coroutineskotlin-multiplatformsqldelight

How to prevent SQLDelight RC-2.0 potential memory leak on mapToList() with coroutineContext?


I am working on a KMM project to understand the usage of Apollo GraphQL and SQLDelight more deeply. I am using layer-by-layer multi modularization and in my domain module, I am trying to map entities I have collected from SQLDelight to my domain model objects in my useCases.

The problem is, SQLDelight 2.0 changes the usage of coroutine-extensions package and currently requires a corutineContext in order to run operations (JUST WHY?).

In the end, I have accepted my fate and decided to inject a dispatcher in order to obtain the coroutine context, however the resulting code turn out like this:


class GetLikedCharacters(
    private val database: RickAndMortDatabase,
    private val coroutineContext: CoroutineContext
) {
    operator fun invoke(
        id: Int
    ): Flow<Resource<List<CharacterModel>>> = flow {
        emit(Resource.Loading)
        try {
            database.getAllLikedCharacters().mapToList(coroutineContext).collect { entities ->
                if (entities.isEmpty()) {
                    emit(Resource.EmptyOrNull)
                } else {
                    val mapped = entities.map { it.toCharacterModel() }
                    emit(Resource.Success(data = mapped))
                }
            }
        } catch (e: Exception) {
            e.message?.let {  message ->
                emit(Resource.Error(message = message))
            }
        }
    }
}

As you can see, in the line: "database.getAllLikedCharacters().mapToList(coroutineContext).collect", I am basically launching a coroutne inside of a coroutine, which can result in a memory leak. I have thought about many ways to prevent this and the most easiest one is to map the query inside my ViewModel, but:

I do not want to rollback to previous versions of SQLDelight as current version is on Release Candidate and us developers will be doomed on new "breaking" API"s in the near future.

So what can be done? Any help is appreciated.


Solution

  • You can use currentCoroutineContext() from the flow {} builder itself. mapToList will run in the scope of the flow then. No need for providing an additional coroutine context in constructor.

    database
      .getAllLikedCharacters()
      .mapToList(currentCoroutineContext())
      .collect {
         // collect here
      }