Open8

Goのコンテキストの思想について

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

Goのコンテキストは「キャンセルされると中断すること」という思想で作ってあるが、終了にも色々ある

  • とにかく速く中断すること
  • 安全に終了すること
  • 発生中の処理内でエラーが起きたような挙動をたどること(ここは制御できないかも。呼び出してる関数がいきなり死ぬ挙動になることが多そう)
Masaki Kato (Duplicate)Masaki Kato (Duplicate)

これはPCでいうと「シャットダウン」「強制終了」「コンセントぶち抜き」のように終了方法がいくつかあるのをイメージしてもらうと分かりやすいかもしれない。コンセントぶち抜きはさすがに致命的な何かを生みかねないが、最低限のデータロストのみを守れるなら強制終了も有力な選択肢となる

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

例えばトランザクションの「中断」とはどのような状態を表すのか?
DatastoreライブラリとSpannerライブラリを例にとって考えてみる

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

これらのライブラリはトランザクションを中断した際にはコミットもロールバックもされないケースが存在する。Commit APIやRollback APIを呼ぶときに渡すコンテキスト自体がキャンセルされるため、トランザクション自体はら残ってしまう。

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

どちらのパッケージもCommit前にコンテキストがキャンセルされた場合には処理は中断されるが、CommitもRollbackもされない。
これはライブラリの仕様によるものである。

ここからは筆者の意見だが、適切にロールバックするというのは「安全な中断」に近い。ロールバックのための処理に時間がかかることがあるためである。
これはライブラリの挙動としてはそぐわないことが多いのではないか。
,ユーザーはコンテキストをキャンセルする時、速やかにゴルーチンが終わることを望んでいることが多いはずである。例えばGraceful Shutdownのような処理では一定以上時間がかかると強制に終了させられることが多く、終了に長い時間がかかり、ユーザーがそれを認識できないこと/ユーザーが調整できないのは望ましくない
・終了にかかる時間は和として表れる。終了に数秒かかるライブラリの中で終了に数秒かかるライブラリを呼ぶような仕組みが多発してしまうと、ユーザーにとっては予期せぬ数十秒の遅延となりうる。

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

・また、安全に終了する仕組みは(どの場合でも有効かは分からないが今回においては)実装することが可能となる。例えばトランザクションに渡すコンテキストを外側のコンテキストとは分けておき、キャンセルの伝播を止めておくなど。これはCommit処理に時間がかかる場合には有効ではないが、ハンドラ内に思い処理がありそれを速やかに終わらせられれば十分であり、②終了するアプリケーションの代わりに別のアプリケーションが同様の処理を再度実行するためトランザクションは残しておきたくない、という時には有効となる。

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

以上のことから、ユーザーにどういった使われ方をされるか分からないライブラリ側の実装は「安全な中断」よりも「迅速な中断」にやりやすいのでは無いかと考察できる。
コンテキストの中断を使う場合には安全に中断されないケースがあることをよく理解し、コードに目を通してから使用することが良さそうである。

Masaki Kato (Duplicate)Masaki Kato (Duplicate)

ライブラリ側の開発者においては、コンセントぶち抜きのようにならないようデータロストがないように最低限の終了処理は実行されるよう設計しつつ、ユーザーコードにすぐ返すような迅速な終了処理を心がけるのが良いかと感じる