テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(akashic report) finchのEndpointの中でブロックしてはならない

finchのstreamingは, 私が必要だと訴求して私が入れたものだから, すぐに使ってみた. ServerにwithStreaming(true)を設定して, あとはArray[Byte]になってるところを, AsyncStream[Buf]にして, あとは適宜それを使う関数を書き換えればいいよね!

  // before
  def write(bytes: Array[Byte]) = {
    files.writeBytes(filePath, bytes)
  }
  // after
  def writeStream(bytes: AsyncStream[Buf]) {
    val readers: AsyncStream[Reader] = bytes.map(Reader.fromBuf(_))
    val writer: ClosableWriter = Writer.fromOutputStream(FileUtils.openOutputStream(filePath.toFile))
    val fut = Reader.copyMany(readers, writer).ensure(writer.close())
    Await.ready(fut)
  }

違った.

finch開発者のヴラジミール氏に助言をもらいながら, WireSharkなどを使ってリクエストを解析していくと, どうやら, クライアントから届いているChunked Streamingは正しいデータを持っている, しかしどうやらAwait.readyで永遠に眠ってしまう.

(マニアックな読者のために: aws-sdk-javaはデフォルトでChunked Streamingを送る. finagle-httpのwithStreamingがデフォルトのfalseであるときはこれがconcatされてからサーバアプリケーションに届くが, trueであるときはそのまま届く. minio/mcはデフォルトでストリーミングを拒否しているので, 挙動に変化はない. ストリーミングを拒否しているのは, V4シグネチャの問題だと思う Add an option to disable chunked encoding. by harshavardhana · Pull Request #581 · aws/aws-sdk-java · GitHub)

結局, ヴラジミール氏も, 上のwriteStreamの実装を知っていながら, 結局最後まで調べなければこの, 理論的には分かっていたはずの結論にたどり着くことが出来なかった. "Don't block an Endpoint" そうこれが答え

https://github.com/finagle/finch/blob/master/docs/best-practices.md#do-not-block-an-endpoint

まとめ: finchを使うときは, Endpointの中でAwaitを使ってはならない. 途中でFutureが生まれる場合は, それをOkなどのOutputまで運んで行く必要がある. 自然と, Futureの関数を使って合成していくプログラミングを要求される.