Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12121] ファイルの概要

このコミットは、Go言語の標準ライブラリ net/http パッケージ内のテストコード serve_test.go における time.Duration の使用方法を改善するものです。具体的には、タイムアウト設定の記述をよりGoらしい慣用的な形式に変更しています。

コミット

  • コミットハッシュ: 9c8923f7dc4b983dca233085560adf4dc079dc25
  • Author: Brad Fitzpatrick bradfitz@golang.org
  • Date: Wed Feb 22 11:22:09 2012 +1100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/9c8923f7dc4b983dca233085560adf4dc079dc25

元コミット内容

    net/http: update test to use time.Duration better
    
    R=golang-dev, dsymonds
    CC=golang-dev
    https://golang.org/cl/5688063

変更の背景

このコミットの背景には、Go言語における時間単位の表現のベストプラクティスへの準拠があります。Goの time パッケージは、時間量を扱うための time.Duration 型を提供しており、これはナノ秒単位の整数として時間を表現します。この型は、可読性と型安全性を高めるために、time.Secondtime.Millisecond といった定数と組み合わせて使用することが推奨されています。

元のコードでは、const second = 1000000000 /* nanos */ のように、ナノ秒を直接整数で定義し、それを浮動小数点数で乗算してタイムアウト値を設定していました。これは機能的には問題ありませんが、Goの慣用的な書き方ではありませんでした。time.Duration を直接使用することで、コードの意図がより明確になり、将来的なメンテナンス性や可読性が向上します。また、浮動小数点数演算による潜在的な精度問題も回避できます。

前提知識の解説

Go言語の time パッケージと time.Duration

Go言語の time パッケージは、時間と日付を扱うための強力な機能を提供します。その中でも time.Duration 型は、時間の長さを表現するために設計された型です。これは int64 のエイリアスであり、ナノ秒単位で時間を内部的に保持します。

time.Duration は、以下のような時間単位の定数と組み合わせて使用することで、非常に可読性の高いコードを書くことができます。

  • time.Nanosecond
  • time.Microsecond
  • time.Millisecond
  • time.Second
  • time.Minute
  • time.Hour

例えば、5秒間は 5 * time.Second と記述でき、250ミリ秒は 250 * time.Millisecond と記述できます。これにより、コードを読むだけでその数値が何を意味するのかが直感的に理解できます。

net/http パッケージと http.Server のタイムアウト設定

net/http パッケージは、HTTPクライアントとサーバーの実装を提供します。HTTPサーバーを構築する際に使用する http.Server 構造体には、クライアントからのリクエスト処理におけるタイムアウトを設定するためのフィールドがあります。

  • ReadTimeout: クライアントがリクエストヘッダーとボディを送信するのにかかる時間の最大値。この時間を超えると、サーバーは接続を閉じます。
  • WriteTimeout: サーバーがレスポンスヘッダーとボディをクライアントに送信するのにかかる時間の最大値。この時間を超えると、サーバーは接続を閉じます。

これらのタイムアウトは、サービス拒否(DoS)攻撃を防いだり、リソースの枯渇を避けたりするために重要です。これらのフィールドは time.Duration 型を受け取ります。

技術的詳細

このコミットは、src/pkg/net/http/serve_test.go ファイル内の TestServerTimeouts 関数における http.Server のタイムアウト設定方法を変更しています。

変更前は、以下のように second という定数を定義し、それを使ってタイムアウト値を計算していました。

const second = 1000000000 /* nanos */
server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second}

ここで 0.25 * second は、浮動小数点数 0.25 と整数 1000000000 の乗算であり、結果は 250000000 となります。これはナノ秒単位で250ミリ秒を意味します。

変更後は、Goの time パッケージが提供する time.Millisecond 定数を直接使用するように修正されました。

server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond}

この変更により、コードの意図がより明確になり、ReadTimeoutWriteTimeout がそれぞれ250ミリ秒であることを一目で理解できるようになりました。また、コメントの /* fudge from 0.25 above *//* fudge from 250 ms above */ に更新され、整合性が保たれています。

この変更は、Goの慣用的なコーディングスタイルに準拠し、コードの可読性と保守性を向上させるためのものです。機能的な変更はなく、テストの動作に影響はありません。

コアとなるコードの変更箇所

--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -245,8 +245,7 @@ func TestServerTimeouts(t *testing.T) {
 		fmt.Fprintf(res, "req=%d", reqNum)
 	})
 
-	const second = 1000000000 /* nanos */
-	server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second}
+	server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond}
 	go server.Serve(l)
 
 	url := fmt.Sprintf("http://%s/", addr)
@@ -277,7 +276,7 @@ func TestServerTimeouts(t *testing.T) {
 	if n != 0 || err != io.EOF {
 		t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF)
 	}
-	if latency < 200*time.Millisecond /* fudge from 0.25 above */ {
+	if latency < 200*time.Millisecond /* fudge from 250 ms above */ {
 		t.Errorf("got EOF after %s, want >= %s", latency, 200*time.Millisecond)
 	}
 

コアとなるコードの解説

変更点1: const second の削除と ReadTimeout/WriteTimeout の設定変更

-	const second = 1000000000 /* nanos */
-	server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second}
+	server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond}
  • 削除された行: const second = 1000000000 /* nanos */
    • この行は、1秒をナノ秒で定義する定数でした。Goの time パッケージが提供する time.Secondtime.Millisecond といった定数を使用すれば、このような手動での定義は不要です。
  • 変更された行: server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond}
    • ReadTimeoutWriteTimeout の値が、0.25 * second から 250 * time.Millisecond に変更されました。
    • 250 * time.Millisecond は、time.Duration 型の値を生成するGoの慣用的な方法です。これにより、250ミリ秒という時間の長さが明確に表現されます。
    • この変更により、コードの可読性が大幅に向上し、数値が何を意味するのかが直感的に理解できるようになります。

変更点2: コメントの更新

-	if latency < 200*time.Millisecond /* fudge from 0.25 above */ {
+	if latency < 200*time.Millisecond /* fudge from 250 ms above */ {
  • 変更された行: if latency < 200*time.Millisecond /* fudge from 250 ms above */ {
    • コメント /* fudge from 0.25 above *//* fudge from 250 ms above */ に更新されました。
    • これは、タイムアウト値の計算が 0.25 * second ではなく 250 * time.Millisecond に基づいていることを反映しています。
    • このコメントの更新は、コードの変更と整合性を保ち、読者がコードの意図を正しく理解できるようにするために重要です。

これらの変更は、Goのコーディング規約とベストプラクティスに沿ったものであり、コードの品質と保守性を向上させます。

関連リンク

参考にした情報源リンク