blog.syfm

徒然なるままに考えていることなどを書いていくブログ

GopherCon 2018 に Mercari BOLD Scholarship で参加・発表してきた

今年の 8 月 26 日から 9 月 1 日の間、Mercari BOLD Scholarship で Go 最大のカンファレンスである GopherCon へ参加していました。

tech.mercari.com

GopherCon 2018 は希望者のみ参加できるワークショップ、3 日間に渡って行われるトークセッション、最終日に行われるライトニングトーク (LT) や Go チームコミュニティにより行われる Go コントリビューションワークショップなどで構成されていました。その他にも Welcome Party などをはじめとする色々なイベントが用意されていました。 実際のスケジュールは以下のページから見ることができます。

www.gophercon.com

自分はワークショップを含む全ての日程に参加していました。以下では参加したワークショップと、特に興味深かったセッション、LT について書いていきたいと思います。

Pre-Conference Workshop: Testing

自分は Go のテスト手法に関するワークショップに参加しました。このワークショップの講師は Mat Ryer 氏で、stretchr/testify の作者です。
Testify は自分がテストコードを書く時に、非常に良く使っているライブラリです。Testify を作った方が、普段どのようにテストについて考え、どういったテストコードを書いていて、どういった思想で Testify を作ったのかが気になったのがこのワークショップに参加した理由です。

github.com

ワークショップの内容は非常に丁寧に作られていて、testing パッケージの testing.Ttesting.M について、go test の実行方法などの Go の初学者向けの話から始まり、Go のテストに関する基本的な知識はほぼ網羅されていたように思います。他のプログラミング言語とは異なり、基本的に Go はテストフレームワークを使わずにテストコードを書くため、このあたりの話は常に役に立ちます。また、一日でテスト技法を学べるくらいの仕様の小ささも Go の良い点だと思います。

基本的な内容にもかかわらず、自分が知らなかった機能や仕様も多くありました。 例えば、パッケージ名が foo のとき、そのテストコードのパッケージは同じ名前にする場合が多いですが、foo_test とすることができるという仕様です。このように変更すると、非公開な型、関数、変数へアクセスすることができなくなるため、ブラックボックステストを行うことができます。 自分は、公開された API の仕様をテストすべきで、非常に変化しやすいロジックには極力テストを書くべきではないと考えています。変化しやすいロジックに対してテストを書くと、そのテストは非常に脆いものになります。

他にも、スタブを書く時に、メソッドのシグネチャを表現した関数フィールドを用意することで期待するレスポンスを簡単に差し替えることのできるテクニックも紹介されていて、これは現在書いているテストでも非常に良く使っています。

例えば、以下のような key-value store のインターフェースが定義されているとして、

type KVS interface {
    Set(k string, v interface{})
    Get(k string) (v interface{}, ok bool)
}

スタブでは各メソッドと同じシグネチャを持つ関数フィールドを定義し、実装の詳細をそちらのフィールドの方で持つことで任意の実装を差し込めるようになります。

type kvsStub struct {
    set func(k string, v interface{})
    get func(k string) (v interface{}, ok bool)
}

func (s *kvsStub) Set(k string, v interface{}) {
    if s.set == nil {
        panic("set field is nil")
    }
    s.set(k, v)
}

func (s *kvsStub) Get(k string) (interface{}, bool) {
    if s.get == nil {
        panic("get field is nil")
    }
    return s.get(k)
}

また、このようなコードを自動生成する Ryer 氏自作のライブラリが紹介されていました。

github.com

テストの基本的な手法が紹介されたあとは、実際に簡単な Web アプリケーションに対してテストを書くハンズオン形式になりました。
この Web アプリケーションは、簡単な KVS で、値の書き込みは非同期で行われます。
Web アプリケーションはテストの実行時間を見積もるのが難しいものの一つです。
書き込んだ値が正しく取得できるかをテストしたい場合、まず第一に考えるのは、書き込んだあと一定時間 time.Sleep をしてから値を取得するような実装です。
しかし、 time.Sleep はブロックするためタイムアウトができません。データベースへの書き込みや読み込みにかかる時間は環境などにより左右されやすいので、適切にタイムアウトができなければいけません。
これを解決するためにチャネルと for + select を使ってタイムアウトを設定するテクニックが紹介されていました。

ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
timeout := time.NewTimer(5 * time.Second)
defer timeout.Stop()

var testErr error

for {
    select {

    case <-timeout.C:
        t.Fatalf("test timed out waiting for success.  last error: %s", testErr)
        return
    case <-ticker.C:
        testErr = test()
        if testErr == nil {
            // test successful
            return
        }
    }
}

その他にも、この Web アプリケーションのインテグレーションテストや、モックするためのテクニックなどが紹介されていました。
全体の流れとしては、愚直に実装した Web アプリケーションとそのテストはあらゆるコンポーネントが密結合しているため、それらを疎結合にしつつ、よりテストしやすくリファクタリングしていく、といった感じでした。

gRPC reflection and grpcurl

トークセッションでは "gRPC reflection and grpcurl" というセッションが面白かったです。
スピーカーの Joshua Humphries 氏 (GitHub: jhump) は gRPC クライアントのデファクトスタンダードである fullstorydev/grpcurl の作者で、GitHub の gRPC オーガニゼーションのメンバーです。また、自分が今回の GopherCon の LT で発表した gRPC クライアントを作るために必要不可欠だった jhump/protoreflect の作者でもあり、今回の彼の発表を非常に楽しみにしていました。

Humphries 氏が働いている FullStory 社では、サーバ間の通信に gRPC を使用しているそうです。また、各バックエンドサーバのアグリゲータとなるサーバがあり、ここの API エンドポイントは非常に変化しやすいものになっていました。
このサーバの API を叩くために CLI ツールを作っていましたが、API のインターフェースが変わるたびに修正・追加が入ったり、ほとんどすべての API へのリクエストが同じようなロジックになっていました。ここで、Humphries 氏は、動的に gRPC API を取得し、動的にリクエストを送信できないかを考え、protoreflect と gRPCurl が生まれました。

Go でも Protocol Buffers はサポートされていますが、JavaC++ 等とは異なり、 dynamic message (メッセージを動的に組み立てるための API) が提供されていません。
また、Protocol Buffers の descriptor (メッセージの構造を表す識別子) も限定的なサポートとなっていました。
彼が開発した protoreflect には、よりリッチな descriptor を提供する desc パッケージ、dynamic message を Go でも使えるようにするための dynamic パッケージが含まれています。また、同じく protoreflect に含まれる protoparse パッケージを使うと protoc へ依存することなく Protocol Buffers ファイルをパースできるようになります。

これらのライブラリと、 gRPC が提供しているリフレクション機能を使って gRPC サーバが提供している API を動的に取得できることを利用し、 gRPCurl が誕生しました。
gRPCurl は、curl と同じようなインターフェースを持つ gRPC クライアントで、前述の gRPC リフレクションと protoreflect を使うことで Protocol Buffers ファイルを読み込まずにサーバとやりとりができます。

自分がよく使っている protoreflect が、どういう目的で、どういう思想で生まれたかを聞くことができたので非常に興味深いセッションでした。

Lightning Talks

GopherCon では毎年 Lightning Talk (LT) 枠があり、開催直前に募集を募っています。
今回 GopherCon に BOLD Internship に参加するにあたり、その条件として LT 枠に応募することになりました。
時間帯ごとに募集枠が設定されていて、そのどこかの枠を選び、審査が通れば確定となります。
どういう LT をするか迷いましたが、自分がつくった gRPC クライアントの Evans について発表することにしました。Evans については春に開催された Go 1.10 リリースパーティーで発表していたので、その時の内容をベースに再構成することにしました。
LT は 7 分以内に収めなければならず、当然英語で喋らなくてはいけません。自分は英語のスキルが乏しいため、とにかく練習するしかない!と思い、限られた時間でスライドとトークスクリプトをつくり、そのあとはひたすら練習していました。
自分たちと同じく GopherCon に参加したメルカリの tenntenn さんと deeeet さんは GopherCon でトークをした経験があったため、二人に発表の際に気をつけることを聞いたりしていました。
また、LT 前日あたりからは tenntenn さん、deeeet さん、osamingo さん、morikuni さん、codehex さんの前で発表練習を行い、スライドの構成や話すスピードなどをチェックしてもらいつつ、安定して話すことができるようになってきました。

f:id:ktr_0731:20180913192522j:plain

LT の発表者へのいつ頃集合すれば良いかなどの連絡は口頭でしかないので注意したほうが良いと思います。自分の場合、余裕を持って 1 時間前くらいに会場入りをしたらすでに発表者席があり、ある程度のスピーカーが集まっていたので非常に焦りました。また、スタッフに自分が発表者であることを伝えると、マイクの準備をしてくれ、このときに PC のミラーリングの設定をしているかどうか聞かれますが、念の為ちゃんと確認しておいたほうが良いと思います。自分の場合、発表練習の際には確実にミラーリングになっていたにもかからわず、本番ではミラーリングが解除されていました…。(謎)

自分の発表の前には Brad Fitzpatrick 氏をはじめとするレベルの高い Gopher だらけで、自分の番が来る前はひたすら緊張していましたが、いざ始まってしまえば後は練習したことをやるだけです。英語が喋れなくても練習すれば喋りたいことは喋れるようになるし、スタッフの英語が聞き取れなくても英語が苦手なことを伝えればゆっくり喋ってくれます。発表したいという気持ちさえあれば、そういったことはどうにでもなると思います。
自分は人前で喋るのがあまり得意ではなく、非常に苦しかったのも事実ですが、終わってみればものすごい経験になったし、発表してよかったと心から思っています。

f:id:ktr_0731:20180913194537j:plain

今回のスカラシップでは、渡航費や一週間分の宿泊費がすべてサポートされていました。自費だと莫大な費用がかかるので、学生だと (社会人でも) 個人で海外のカンファレンスへ行こうとしてもなかなか難しいと思います。しかし、こういったスカラシップを利用することで学生のうちから非常に貴重な機会を得ることができます。
ぜひその機会を無駄にせずどんどん挑戦して欲しいなと思います!