Go言語でよく見かける以下のコードは、コンテキストを使ってタイムアウトを設定する処理です。
ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)
この行の役割と動作を順を追って説明します。
1. コンテキスト(context.Context
)とは?
コンテキストは、処理の実行に関連する情報や制御を管理する仕組みです。主に以下の用途で使われます:
- タイムアウト: 指定時間内に処理が終わらない場合、自動的にキャンセルする。
- キャンセル: 処理を途中で中断する。
- 値の伝達: 処理中に特定の値を共有する。
Goでは並行処理(ゴルーチン)を多用するため、コンテキストを使ってリソースの無駄遣いを防ぎ、処理を効率的に制御します。
2. context.WithTimeout
の役割
context.WithTimeout
は、指定された時間内に処理が終わらない場合に自動的にキャンセルされるコンテキストを作成します。
-
引数:
-
ctx
: 親コンテキスト。既存のコンテキストを指定する。 -
timeout
: タイムアウト時間。time.Duration
型で指定する(例:5 * time.Second
)。
-
-
戻り値:
-
ctx
: タイムアウトが設定された新しいコンテキスト。 -
cancel
: タイムアウトを手動でキャンセルする関数。
-
3. ctx, cancel :=
の意味
Goの多重代入を使い、context.WithTimeout
の戻り値(ctx
と cancel
)を同時に受け取ります。
-
ctx
: タイムアウトが設定された新しいコンテキスト。 -
cancel
: タイムアウトを手動でキャンセルする関数。
4. 具体的な動作
この行の動作をステップバイステップで説明します。
ステップ 1: タイムアウト付きのコンテキストを作成
ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)
-
context.WithTimeout
を呼び出し、指定されたタイムアウト時間(c.ctxTimeout
)が設定された新しいコンテキスト(ctx
)を作成する。 - 同時に、このコンテキストを手動でキャンセルする関数(
cancel
)も返される。
ステップ 2: キャンセル関数を遅延実行
defer cancel()
-
defer
は、関数の終了時に指定された処理を実行するキーワード。 - ここでは、
cancel
関数を遅延実行し、関数の終了時に必ずコンテキストがキャンセルされるようにする。
5. なぜこれが必要か?
この処理は、以下の問題を解決します。
タイムアウトの必要性
- 長時間かかる処理(例: データベース接続、外部APIリクエスト)が終わらない場合、リソースの無駄遣いを防ぐ。
キャンセルの必要性
- タイムアウトが発生した場合や処理が不要になった場合、コンテキストをキャンセルして関連する処理を中断する。
- リソースの解放やクリーンアップを確実に行う。
6. 具体的な例
以下は、この処理を使った例です。
func (c createAccountInteractor) Execute(ctx context.Context, input CreateAccountInput) (CreateAccountOutput, error) {
// タイムアウトを設定(例: 5秒)
ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)
defer cancel() // 関数の終了時にキャンセルを実行
// 新しいユーザーを作成
user := domain.NewUser(
domain.UserID(domain.NewUUID()),
input.Name,
input.Email,
time.Now(),
)
// ユーザーをデータベースに保存
createdUser, err := c.repo.Create(ctx, user)
if err != nil {
// エラーが発生した場合、エラーを返す
return c.presenter.Output(domain.User{}), err
}
// 正常に処理が完了した場合、結果を返す
return c.presenter.Output(createdUser), nil
}
動作の流れ
-
context.WithTimeout
で5秒のタイムアウトを設定。 -
defer cancel()
で、関数の終了時にキャンセルを実行。 - データベースへの保存処理(
c.repo.Create
)が5秒以内に終わらない場合、コンテキストがキャンセルされ、処理が中断される。 - エラーが発生した場合、エラーを返す。
- 正常に処理が完了した場合、結果を返す。
7. まとめ
-
context.WithTimeout
: 指定時間内に処理が終わらない場合、自動的にキャンセルするコンテキストを作成。 -
ctx, cancel :=
: 多重代入で新しいコンテキストとキャンセル関数を受け取る。 -
defer cancel()
: 関数の終了時に必ずキャンセルを実行。
この仕組みを使うことで、長時間かかる処理を制御し、システムの安定性とパフォーマンスを維持できます。