Go 1.11 の go mod download を速くする

Go 1.13 から Go module mirror と Go checksum database が導入され go mod download が高速化されました。

https://blog.golang.org/module-mirror-launch

しかし Go 1.11 ユーザーはこの恩恵を受けられません。
そこで Go 1.11 の go mod download を速くするために試した結果をご紹介します。

(2021 年 7 月 4 日追記)
この記事はローカル環境での go mod download を検証したものですが GAE/Go 1.11 デプロイ時に適用する方法をコメントして頂きましたのでぜひそちらもご確認ください。
@tomoemon さん、貴重な情報をありがとうございます！

(2022 年 5 月 3 日追記)
[gcloud 383.0.0](https://cloud.google.com/sdk/docs/release-notes#38300_2022-04-26) から beta 指定しなくても app.yaml で [build_env_variables](https://cloud.google.com/appengine/docs/standard/go111/config/appref?hl=ja#runtime_and_app_elements) を指定できるようになりました。

> Enables build environment variables to be set in app.yaml.

## 短い結論

- プライベートリポジトリに対する依存がない場合は環境変数 `GOPROXY` に `https://proxy.golang.org` を設定する。
- プライベートリポジトリに対する依存がある場合はモジュールダウンロードを Go 1.13 にやらせる。

## 測定方法

`golang:1.11` の docker コンテナ内で [golangci-lint](https://github.com/golangci/golangci-lint) を git clone したのち `time go mod download` を 3 回実行して最も速かった時間を記録しました。
また `time go mod download` 実行前に `go clean -modcache` でキャッシュをクリアしています。

```console
$ export GO111MODULE=on
$ git clone https://github.com/golangci/golangci-lint.git
$ go clean -modcache
$ cd golangci-lint
$ time go mod download
```

golangci-lint を使用したのは GitHub でホストされている多数の外部パッケージ (linter) に依存しており go mod download の速度を調べるのに適していると考えたからです。

※ Go 1.11 でも golangci-lint のモジュールダウンロードはできますがビルドはできません。

## 試した方法

### デフォルト (HTTPS)

何も設定せずに go mod download を行いました。
Go 1.11 の go mod download は HTTPS で各パッケージのリポジトリからデータを取得します。

### GOPROXY

> しかし Go 1.11 ユーザーはこの恩恵を受けられません。

冒頭に上記のように書いたのですが実は Go 1.11 でも 環境変数 `GOPROXY` を設定することで Go module mirror を利用できます。

```console
$ export GOPROXY=https://proxy.golang.org
```

紹介した [Go Blog の記事](https://blog.golang.org/module-mirror-launch) にも次のように書かれています。

> If you are running an earlier version of the go command, then you can use this service by setting GOPROXY=https://proxy.golang.org in your local environment.

ただし Go 1.13 以降と異なり、次の点に注意が必要です。

- 環境変数 `GOPROXY` にカンマを含めることができません。
- 環境変数 `GOPRIVATE` をサポートしていません。

これらの違いはプライベートリポジトリに対する依存がある場合に問題になります。

### SSH

GitHub への接続に SSH を使用する設定を行いました。
SSH や git の設定についてはあちこちで紹介されているので省略します。

今回は次のように GitHub でホストされているすべてのリポジトリが対象となるように設定しました。

```console
$ git config --global url."ssh://git@github.com/".insteadOf "https://github.com/"
$ cat .gitconfig
[url "ssh://git@github.com/"]
        insteadOf = https://github.com/
```

### SSH (ControlMaster auto)

GitHub に対する SSH 接続を使いまわす設定を追加しました。

```
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%r@%h-%p
  ControlPersist 10
```

設定している内容については ControlMaster で検索すると記事が見つかると思います。

https://stackoverflow.com/questions/20410252/how-to-reuse-an-ssh-connection

### Go 1.13 併用

モジュールダウンロードを Go 1.13 にやらせるという方法です。

Go 1.13 は次の手順でインストールしました。
`.tar.gz` ファイルを直接ダウンロードして適切な場所に展開してもよいと思います。

https://golang.org/doc/manage-install

```console
$ go get golang.org/dl/go1.13.15
$ go1.13.15 download
```

インストールすると `go1.13.15` コマンドが利用できるようになります。

```console
$ go1.13.15 clean -modcache
$ time go1.13.15 mod download
```

環境変数で明示的に設定していない場合 Go 1.11 と Go 1.13 はモジュールキャッシュを共有しているため `go1.13.15 mod download` を実行後に `go mod download` を実行してもダウンロードは発生しません。

共有しても大丈夫なのかという疑問はあると思いますがモジュールキャッシュはダウンロードしたファイルの格納場所であるとドキュメントに記載されています。

https://golang.org/ref/mod#module-cache

Go 1.15 のリリースノートにも次のような記載があるので異なるバージョンの Go がキャッシュを共有することは想定内だと考えています。

https://golang.org/doc/go1.15#go-command

> The workaround is not enabled by default because it is not safe to use when Go versions lower than 1.14.2 and 1.13.10 are running concurrently with the same module cache.

## 結果

ネットワーク帯域が安定していなかったり測定した時間が異なるので精度は低いものと考えてください。

| 実行方法                 | 処理時間 (秒) |
| ------------------------ | ------------- |
| デフォルト (HTTPS)       | 332.753       |
| GOPROXY                  | 28.808        |
| SSH                      | 384.548       |
| SSH (ControlMaster auto) | 379.934       |
| Go 1.13 併用             | 94.846        |

GOPROXY すごい。

GOPROXY と Go 1.13 以外は PC の調子が悪かったのか回線が貧弱だからか途中でエラーになってしまうため成功するまで go mod download を繰り返しました。
記載している処理時間は成功するまでに実行した go mod download の合計になります。

SSH は HTTPS より遅い結果になりました。
残念ながら ControlMaster auto 設定もあまり効果がないようです。
(設定不備あるいは GitHub 側の制限の可能性もあります)

Go 1.11 (GOPROXY) と Go 1.13 の違いがどこで出ているのかも分かってません。

## まとめ

プライベートリポジトリに対する依存がない場合は環境変数 `GOPROXY` に `https://proxy.golang.org` を設定することで Go 1.11 による go mod download が大幅に高速化されることがわかりました。

また、プライベートリポジトリに対する依存がある場合でもモジュールダウンロードを Go 1.13 で行うことで Go 1.11 のみより短時間でダウンロードできることがわかりました。

この記事が GAE/Go 1.11 に魂を縛られている同志 [^1] のお役に立てば幸いです。

[^1]: https://cloud.google.com/appengine/docs/standard/go/go-differences