Kotlin Coroutines のために AWS SDK for Java でノンブロッキングを実現する

アスクルの こたにん (@Kotanin0) です。

Kotlin で AWS SDK を利用するには、今現在はJavaのものを使う必要がある

KotlinでAWSのいろいろを操作する際に、AWSが提供しているSDKを利用すると便利です。
が、2022年3月現在、Kotlin向けのAWS SDKはプレビュー版で、正式リリースがされていません。
AWS SDK for Kotlin (デベロッパープレビュー)

そのため、今現在は AWS SDK for Java を使う必要があります。
AWS SDK for Java

AWS SDK for Java 2.x の非同期プログラミングはブロッキングされる

AWS SDK for Java の最新メジャーバージョンの2.系には、非同期プログラミング向けの機能が搭載されています。
Asynchronous programming - AWS SDK for Java 2.x Developer guide

だがこれは、Kotlin CoroutinesやSpring WebFluxのようなリアクティブプログラミングと組み合わせるには注意が必要です。

suspend fun hasObject(): Boolean = coroutineScope {
  val request = ListObjectsV2Request.builder()
    .bucket(bucket)
    .build()

  val result = s3AsyncClient.listObjectsV2(request).join() // <- blocking :(
  result.contents().size > 0
}

上のようなコードがあったときに、.join()がブロッキングコードになってしまうのです。

AWS SDK for Java でノンブロッキングを実現する

s3AsyncClientに用意されているメソッドたちは、基本的にCompletableFuture<T>を返すつくりになっています。
Interface S3AsyncClient

なので、CompletableFuture<T>suspend funに置き換えてあげるとよいです。
Kotlin KEEP に、コールバック関数と中断関数の変換に関するとてもわかりやすいサンプルが載っています。
droidkaigi にも「コールバックを使った既存のAPIを中断関数に置き換える」例がありますので合わせてご覧ください)

suspend fun <T> CompletableFuture<T>.await(): T =
  suspendCoroutine<T> { cont: Continuation<T> ->
    whenComplete { result, exception ->
      if (exception == null) // the future has been completed normally
        cont.resume(result)
      else // the future has completed with an exception
        cont.resumeWithException(exception)
    }
  }

JavaのCompletableFuture.whenCompleteコールバックで、KotlinのContinuation.resume/resumeWithExceptionを返すという処理です。
これによって、Javaのコールバック関数を、Kotlin Coroutines上で中断関数として扱えます。

suspend fun hasObject(): Boolean = coroutineScope {
  val request = ListObjectsV2Request.builder()
    .bucket(bucket)
    .build()

  // val result = s3AsyncClient.listObjectsV2(request).join()
  val result = s3AsyncClient.listObjectsV2(request).await() // non-blocking :)
  result.contents().size > 0
}

これでノンブロッキング。
ブロッキングコードの検出については BlockHound で行いました。 BlockHoundは改めて記事にできたらしようかな。

まとめ

Kotlin Coroutines を使う場合は、非同期プログラミングの旨味が失われてしまわないように、ブロッキングコードの取り扱いには十分気をつけましょう。

AWS SDK for Kotlin 早くリリースされないかなぁ。。。

ASKUL Engineering BLOG

2021 © ASKUL Corporation. All rights reserved.