Upgrade to Pro — share decks privately, control downloads, hide ads and more …

あなたはContextの挙動を説明できますか?

 あなたはContextの挙動を説明できますか?

GoのContextの細かい挙動の数々を学ぶスライドです。

Yoshiki Shibukawa

December 04, 2019
Tweet

More Decks by Yoshiki Shibukawa

Other Decks in Technology

Transcript

  1. 2 お前誰よ? Yoshiki Shibukawa  Honda R&D:〜Dec/2010  DeNA:〜Aug/2017  Future Corporation:Sep/2017〜  Family:

    wife and three daughters Books  つまみぐい勉強法、  Real World HTTP、Mithril、  Goならわかるシステムプログラミング Translations  Expert Python Programming  Agile Software Development with Scrum  Pomodoro Technique Illustrated  The Art of Community etc Favorite Languages  JavaScript® / Go / Python Hobbies  Inline Skating Account  github.com/shibukawa  twitter.com/shibu_jp
  2. 有名企業のお客様のシステムを多数手がけています • 日本有数のイノベーション・デザインファームi.labをグループ会社化 • 長野銀行様の融資営業支援システムのAWS全面移行 • 佐川急便様の配送伝票入力自動化AIシステムが本稼働 • 全日食チェーン加盟店様向けスマートフォンアプリ『全ちゃんナビ』を開発 •

    「みんなのプロ野球情報ステーション『キューステ!』」をオープン! • ワコール様の新接客サービス「3D smart & try」のオムニチャネル基盤を開発 • ORBIS様アプリのパーソナルAIメイクアドバイザーにDL技術提供 • 外為どっとコム様店頭FXサービス「外貨ネクストネオ」にFutureVuls導入 • ABCマート様の販売スタッフ向けスマフォアプリ「s NAVI」を全店リリース • あとは東京カレンダーとか、コードキャンプ、TrexEdgeが関連会社です 弊社プレスリリースサイトから抜粋 https://www.future.co.jp/press_room/
  3. Sponsor of Go Conference 2019 My Advent Calendar will describe

    Tofu on Fire of Go Conference 2019 Autumn
  4. context.Context • Goにcontextが入ってはや3年 • 使い方のプラクティスを知っている人は増えている(と思う) ◦ 時間のかかる処理を実行する関数の第一引数には必ず ctx context.Context と書く

    ◦ ctx.Done() を待つことで親側のキャンセルを検知する ◦ タイムアウトの処理は今どきは timeパッケージを使うのではなく、 context.WithTimeout や context.WithDeadline を使う ◦ context.WithValue() はあまり積極的には使わない ◦ 構造体には保存しない(特別なケースを除いて) • Contextのモックを作ってみようと思って挙動を調べてみました ◦ そして敗北しました
  5. Contextについて理解する (1) • 基本的なところ ◦ 2種類のルートとなるContextのファクトリ関数がある ▪ context.Background() ▪ context.TODO()

    ◦ 4つの親Contextを受け取り、子Contextを作るファクトリ関数がある ▪ context.WithCancel() ▪ context.WithTimeout() ▪ context.WithDeadline() ▪ context.WithValue() • 親子関係が作れる Background WithDeadline WithValue WithCancel WithDeadline WithTimeout
  6. Contextのモックを作るには? • Stack Overflowにそのような質問があり、サンプル実装が回答されていた ◦ Mock context.Done() in unit test

    type mockContext struct{} func (ctx mockContext) Deadline() (deadline time .Time, ok bool) { return deadline, ok } func (ctx mockContext) Done() <-chan struct{} { ch := make(chan struct{}) close(ch) return ch } func (ctx mockContext) Err() error { return context.DeadlineExceeded } func (ctx mockContext) Value(key interface{}) interface{} { return nil }
  7. • とはいえ、これではうまくいかないことが ◦ 常にタイムアウトしているので、 3秒後のリクエストは成功するが、 10秒後のリクエストは失敗するみ たいなケースのテストには使えない • いっそのこと時間の概念を全部外挿できるパッケージを作ってみては? ◦

    作った→ https://github.com/shibukawa/itime ▪ Advanceというメソッドで任意に時間をすすめることができるモック時間ライブラリ ▪ 本物のtimeパッケージラッパーも提供しているよ ◦ DummyのContextも実装してみた ◦ まだ荒削りなので挙動がおかしいことがたまにある ◦ 本当に正しく動かすには難しい ▪ 時間を進めた後、時間待ちのロジックに処理が回ったかどうかという判定はむずい • 停止性問題? • runtime.Gosched()を10回呼んで見るとかやっているが・・・
  8. 今考えていること • いっそのこと、実際に実タイマーでテストしたほうがテストの挙動としては安定しそう ◦ runtime.Gosched()を何回実行すれば期待するところまでアプリコードが進むかが分からない ◦ モックタイマーの場合、実時間の 1000倍で時間が進む、みたいな実装の方が良さそう ◦ ユニットテストとしては、時間のシナリオを設定して走らせるよりは実際に何かが発生したタイミング

    を可視化するほうがわかりやすいのではないか? ▪ テストシナリオ、実行されるコード、その期待する結果、みたいなのをプレーンテキストで DSL で書くと、なにをどうやっても理解に訓練が必要なものしかできない ▪ ソースコードは1次元の行に縛られたテキストファイルなので複数のフローを一緒に書くのは 難しい ◦ 実際のタイマーに依存するのはテスト実行時間が伸びるが // +build ciをつけて go test -tags ciのときだけ実行するとかすればいいのでは?