PHPerKaigi 2024に参加した #phperkaigi

今年も参加してきました。

phperkaigi.jp

聴講した発表へのフィードバックも終わり、このエントリで私のPHPerKaigi 2024はフィニッシュです。

#runn開発者会議

最近恒例の廊下での会議ですが、テストランナーのシンタックスの改善の方向性についてどうしてもしめじさん( @smeghead さん)から直接フィードバックをもらいたく、3日間「いつか会えるだろう」とフラフラしていました。 気にかけていただいた皆さんありがとうございました。無事邂逅できました!

runnのランブックの一級言語がYAML(?)なのでYAMLシンタックスをうまく使ってかつニーズを満たす構文が思いつけばいいのですが、もう一歩アイデアが降ってこない。。。というところまででした。しめじさんにも言われたのですが「一度作ってしまうとなかなか変えられない」ものなのでどうにか降りてきてほしいっ!!!

あとはカンファレンス駆動で、runnで使っているOpenAPIパーサパッケージの差し替えとか、変数展開に使っているデリミタのエスケープができるようにするなどして、とうとうv0.100.0になりました。

v1...

github.com github.com

キャッシュとCSRF

Day2のお昼は話したい人が目の前にいたので声かけた結果なかなかなさそうなメンツで行くことに。

CSRF対策としてのOriginヘッダの話で、 @hiro_y さんの発表と全く同じことを(おそらく発表を聞いていない上で)言っている @catatsuy さんがいて、「あーこれは古来からのCSRFトークンではない対策もアリだなあ」と思ったのでした。

「そーだいさんのキャッシュ話面白かった」というところからキャッシュ全般の話になり「最近のJSフレームワークやGraphQLクライアントは勝手に(?)キャッシュをしてくるのでムズカシイ(かなり意訳)」と、なるほどと聞いていました。

プロキシキャッシュの話ももう少し聞けば良かった...!

そんな話をしながら「普通に衣上下に縦に食べられない厚さのカツ」を体験できました。おいしかった。@ttabtt3 さんチョイスありがとうございました。

発表

今回出したプロポーザルはCLIツールのデザインについての話でした。発表内容は資料や動画を見ていただくとして、多くの皆さんに質問や反応をしてもらって良かったです。

speakerdeck.com

CLIツール、小さく作れるし、しっかり考えて作ると勉強になるし、Webアプリケーション開発にも通じるものが多いのでおすすめです。

コミュニケーション

時間制限をして思いつく限りを書いてみました

無事きんじょうさん( @o0h_ さん)を拉致(?)して懇親会に行けたのは良かったです。 私はきんじょうさんを知識蓄積型、広範囲学習型とみているのですが、いつも発表の内容が明快でかつ多彩で気になっていた方でした。 今回も「多分何聞いても回答が返ってきそう」と思って、きんじょうさんに色々質問していました。 ただ、すでに私にお酒が入っていたので、質問しきれたかというとそうでもないので、次回はカンペを用意していこうと思います。

@kaz_29 + @keita__Max のお二人の話も面白かったです。どこかのカンファレンスでネタになるかもしれないのであまり話せませんが、良いシナジーだなあと思いました。 あと、これも私からは詳細は書けませんが、みなさん @keita__Max さん見かけたら話しかけてみると良いと思います。めちゃくちゃすごかったし面白かった!

すごいといえば @katzchum さんと @tyamahori さんの「あるあるですが〜」から話していた全然あるあるではない「フィーチャーフラグ Lv100(命名 きんじょうさん)」は驚愕だった。たぶんこれもいつかカンファレンスで出てくると予想しています。

@77web さんと @KentarouTakeda さんに凸って聞いた「SymfonyとLaravel」話もめちゃくちゃ興味深かったです。一部 @77web さんがブログエントリに書かれていたので是非。書けない話もありますよね。

https://77web.hatenablog.com/entry/2024/03/11/200747

そういえば、そーだいさんの近くにいるといつも良い話を聞けるので良いです。ただ、いつもそうなので、良い話をするそーだいさん「以外」がいるのか気になっています。

あと、P山さんには「謎の世界的なオヤマ」推しをされてとても困りました。

ただ、印象に残したのは私のようです。

daisuki.nichiyoubi.land

これまたk1LoWさんとの交流によってもたらされたやつですが、「P山さんの凄さ」について聴いたのがすっっっごい心に残ってます。

こちらの凄い人を体験したい方はこちらへ

PHPerKaigi

会期中のタイムラインでも、廊下でも話題になった話なのですが、どのカンファレンスもいつも楽しいのですが、PHPerKaigiの懐の深さと楽しさは本当に素晴らしいと思います。

「PHPer」と冠していますが、もっとひろくエンジニアの人々に開かれているので、PHPerだったときからPHPerではない?ように感じる今でもとても居心地が良いです。

また来年も1年の集大成をぶつけられれば良いなあと思っています。

スタッフの皆さん、PHPerKaigiコミュニティのみなさんありがとうございました!

contextのキャンセル時にtesting.T.Cleanupのようにクリーンアップ処理を実行できるパッケージdonegroupを作った

contextのキャンセルとそれに対応したクリーンアップ処理

Goにおいてcontextはさまざまな値を伝搬させるために使用します。その1つがキャンセル信号です。

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

contextを通じてキャンセル信号を送ることによって、そのcontextが伝播した関係するある種のcontextグループでキャンセルに対応した処理を行うことができます。

このcontextのキャンセルに対応した処理を、このエントリでは「クリーンアップ処理」と呼ぶことにします。

クリーンアップ処理の具体例: Graceful shutdown

あるミドルウェア(プロキシサーバでもデータベースサーバでもよい)があったとして、そのミドルウェアにはメインの機能以外に「ログやメトリクスを定期的にまとめて外部サーバに転送する機能(ログ転送機能)」があるとします。

ミドルウェアのプロセスを停止する際に、ログ転送機能が転送中であれば転送完了までプロセスの停止を待って欲しいですし、次の起動時のためにどこまで転送完了したかの記録するまでプロセスの停止を待って欲しいと考えます。

このような、いわゆる「Graceful shutdown」の実現に必要な後始末処理は、本エントリにおける「クリーンアップ処理」の1つの具体例になります。

cancel() はキャンセル信号の伝搬しかしない

キャンセル処理の伝搬は大抵 context.WithCancel() で作成した cancel() を使って行います。

cancel() を実行することでcontextや、そのcontextから派生した子contextにキャンセル信号が伝搬され、その信号は context.Done() から補足できます(実体は <-chan struct{})。

contextを引数で受け取っている各処理は、context.Done() から信号を受け取ったときにクリーンアップ処理を行うようにする(処理を書く)のですが、このクリーンアップ処理の実行の完遂は context.WithCancel()cancel() で実現しているキャンセル信号の伝搬の仕組みでは保証されません。

次のGo Playgroundに簡単な例を置きました。クリーンアップ処理が完了する前に main() が終了してしまい、クリーンアップ処理が完了していないことがわかると思います。

https://go.dev/play/p/F-4qnF5L9bf

クリーンアップ処理の実行を保証したい

少し複雑なGoのプログラムを書くと context はさまざまな箇所に渡されます。「(終了処理実現のために)関数の第一引数にcontextを渡す」という慣習もありますし、その深さ(どれくらい関数をまたいでcontextが渡されてきているか)は意図せず深くなっていきます。

また、goroutineの存在により並行処理もよく活用します。開始した並行処理は終了処理を書く必要があり、必要であればクリーンアップ処理を書く必要があります。そもそも context を渡しているのはgoroutineによる並行処理の終了処理のためでもあります。

終了処理のトリガーにはcontextのキャンセルを検知できる context.Done() を使います。

では、クリーンアップ処理はどう実装したら良いでしょう?クリーンアップ処理の完了を待つための仕組みが必要です。

testing.T.Cleanup

ところで、testing.T.Cleanup() はテストのクリーンアップ処理を保証するための仕組みです。t.Cleanupでクリーンアップ処理を登録しておくことによって、テスト終了時にそれらのクリーンアップ処理を実行してくれます。

私はcontextの文脈でもこの testing.T.Cleanup() のような機能が欲しいと考えました。

donegroup

上記のようなモチベーションで作成したパッケージが donegroup です。

github.com

donegroupはcontextのキャンセル時にtesting.T.Cleanupのようにクリーンアップ処理を実行できる仕組みを提供するパッケージです。

準備

まず context.WithCancel() の代わりに donegroup.WithCancel() を使ってcontextとcancel()の対を作成します。

そして、cancel() 実行後にクリーンアップ処理を待つため、donegroup.Wait() を実行します。

これだけで準備は完了です。

具体的には

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

ctx, cancel := donegroup.WithCancel(context.Background())
defer func() {
    cancel()
    if err := donegroup.Wait(ctx); err != nil {
        log.Fatal(err)
    }
}()

に書き換えるだけです。

クリーンアップ処理の登録( donegroup.Cleanup )

クリーンアップ処理の登録も簡単です。任意の場所で donegroup.Cleanup() を使ってクリーンアップ処理を登録するだけです。

err := donegroup.Cleanup(ctx, func(_ context.Context) error {
    // 何かしらのクリーンアップ処理
    fmt.Println("cleanup")
    return nil
})

これだけで、contextのキャンセル時にクリーンアップ処理の実行をしてくれます。クリーンアップ処理はいくつでも登録できます。testing.T.Cleanup() との違いはクリーアップ処理の実行順序の保証がないということだけです。

関数実行の保証( donegroup.Awaiter / donegroup.Awaitable )

donegroupは、次のように「実行に時間がかかる処理の実行を待つ(保証する)」こともできます。 この場合は donegroup.Awaiter() を使います。completed() が実行されるまで donegroup.Wait() は待ってくれます。

ctx, cancel := donegroup.WithCancel(context.Background())

go func() {
    completed, err := donegroup.Awaiter(ctx)
    if err != nil {
        log.Fatal(err)
        return
    }
    // 実行に時間がかかる処理
    time.Sleep(100 * time.Second)
    fmt.Println("do something")
    completed()
}()

// メインの処理
fmt.Println("main")
time.Sleep(10 * time.Millisecond)

cancel()
if err := donegroup.Wait(ctx); err != nil {
    log.Fatal(err)
}

fmt.Println("finish")

// Output:
// main
// do something
// finish

「この関数ブロックの実行を保証したい」というような場合は defer donegroup.Awaitable(ctx)() が使いやすいです。

go func() {
    defer donegroup.Awaiter(ctx)() // この関数ブロックの実行を保証する。
    if err != nil {
        log.Fatal(err)
        return
    }
    // 実行に時間がかかる処理
    time.Sleep(100 * time.Second)
    fmt.Println("do something")
}()

クリーンアップ処理の実行待ちにタイムアウトを設定したい

donegroup.Wait() の代わりに donegroup.WaitWithTimeout() を使うことでクリーンアップ処理待ちにタイムアウトを設定することができます。

この場合、donegroup.Cleanup()donegroup.Awaiter() donegroup.Awaitable()タイムアウトが伝播します。

具体的には donegroup.Cleanup() に渡すクリーンアップ処理 func(ctx context.Context) error の ctx にキャンセル信号が届くので ctx.Done() を使って適切にキャンセル処理を書きましょう。

donegroupのメリット

donegroupのメリットは、contextさえ渡しておけば関数シグネチャなどを変えずにいつでもクリーンアップ処理を追加実装できる点です。

また、クリーンアップ処理のエラーもまとめて受け取れます。

実装の深い場所でクリーンアップ処理が必要な並行処理を実装する場合も安心です。

というわけで

散らばっている並行処理の後始末に是非使ってみてください。

octocov-action v1 をリリースしました

追記 with.workdir:with.work-dir: に変更になっています

octocov-action v1 をリリースしました。octocov-actionはoctocovGitHub Actionsで使う際に使用するActionです。

github.com

今回、何が変わったかを紹介します。

Docker container actionからComposite actionに変更

Actionの実装方式をDocker container actionからComposite actionに変更しました。

これにおける効果は大きく2つあります。

octocovのバージョン指定が可能になった

1つ目は octocov のバージョンを柔軟に指定できるようになったことです。

というのもDocker container actionで ghcr.io/k1low/octocov を使っている限りはActionの外部からoctocovのバージョンを指定することができませんでした。

バージョン指定ができないと何が困るかというと、最新のoctocovでバグや意図しない動作が発生した際に1つ前のバージョンに固定するというワークアラウンドがとれないことです。

そのため、従来は octocov のバージョンと octocov-action のバージョンを同期させることで、バージョン指定ができるようにしていました。

今回 Composite actionにして任意のoctocovを指定できるようになりました( with.version: セクション )。

Actionのセットアップ速度が向上した

2つ目の効果です。

Docker container actionの場合Docker imageのpullとコンテナの起動が必要ですが、それが必要なくなりました。

そのかわり、途中でoctocovのリリースバイナリを取得する必要があるのですがそれでも体感で速いです。

今後リリースバイナリのキャッシュなどをするようにするとより速くすることが期待できます。

octocovの実行ディレクトリを指定できるようになった

with.work-dir: セクションを指定することでoctocovの実行ディレクトリを指定できるようになりました。モノレポな環境で便利だと思います。

このような新機能が追加しやすくなったのもComposite actionに変えた効果ともいえます。

Docker container actionのActionの今後

octocovがv0.xである限りはDocker container action版のoctocov-actionもメンテナンスしていく予定です。従来通り uses: k1LoW/octocov-action@v0 で使用できます。

というわけで

octocov-action v1 をリリースしました。 uses: k1LoW/octocov-action@v1 で何も設定を変えずにすぐに使用できますので是非ご利用ください。

今後もoctocovをどうぞよろしくお願いします*1

*1:そういえば、もし活用している企業様がいらっしゃいましたらこそっとでいいので教えてください。単純に喜びます

2023年の振り返りと2024年の抱負

2023年の振り返り

2023年は私にとっていろいろ思いを巡らせる年となりました。将来とか家族とか人生とか。内容は割愛しますが、HIPHOPクラシックを1つだけ貼っておきます。

open.spotify.com

2023年の抱負は「書く」でした。はじめてZenn Bookを書いたりなどしましたが、他にはそこまで書いていない気がします。ムズカシイ。

zenn.dev

あ、でも2023年後半に久しぶりに「描く」ことはできてとても嬉しかったです。

OSS

2023年も少なくなったかなと思います。

一方で、年初の宣言通り runn の開発を継続していました。 #runn開発者会議 もオンラインオフライン問わず何回も開催することができましたし、APIテストツールとしてイベントに呼ばれたり(runn開発者以外の)企業で使ってもらったりと、広く評価してもらえるようになりました。 一緒に開発してくれている@katzchumさんありがとうございます。

また、2manymws orgもプロダクションで使うパッケージとして鍛えてもらいました。 @pyama86さんありがとうございます。

今年の趣味OSSはどうなるんだろう?

でもrunnをもう少し良くしたい。v1にできる勇気が出ると良いなあ。

発表

個人的には頑張ったと思います。

私は体系的に学んだことを軸に発表するというのが苦手です。その分、多少王道から外れていても、自分で考え、作り、感じたことを集めてそのエッセンスを共有できると良いなと思っています。 2023年はどれも「自分こそが発表するべき」と思える特徴というかクセのある内容になったと思っています。

2024年の抱負

今年はなんだろう...

漠然と「新しい気持ちになりたい」というのはあるんですけど、「何を」「どうやって」というのは思いついていないし、「"気持ち"ってなんだよ。それってあなたの感想ですよね™️」という気持ちです。

とりあえずブラックフライデーに購入して深圳から届いたものの今の今まで手をつけていないブツが机の後ろに鎮座したままなので、それにちゃんと手をつけたいです。新しい気持ちにはなりそう。

今年もよろしくお願いします。

GoのHTTPミドルウェアやその周辺パッケージを配布する新しいGitHub Orgをはじめました

このエントリは GMOペパボエンジニア Advent Calendar 2023 および、 Go Advent Calendar 2023 シリーズ3 の19 日目の記事です。

以下のエントリでも少し触れられていますが、現在プロキシサーバをGoで書くプロジェクトがあります。

ten-snapon.com

k1low.hatenablog.com

主な実装をしているのは @pyama86 さんで、それはもうブルドーザーのように実装が進んでいるわけですが*1、私も少しだけですが書いています。

必要そうな機能をGoの薄いHTTPミドルウェアハンドラとしてOSSとしていくつか切り出していました。

そして、本体の実装が進むにつれて足りない機能を Pull Request ベースでもらったりしていたのですが、関連OSSを作る必要が発生したりなど個人リポジトリだとまとまらないため、それらをまとめるためだけにGitHub Org を新設しました。

github.com

既に私のリポジトリからのトランスファーは済んでおり、今後はこのOrg上で開発していく予定です。

どうぞよろしくお願いします。

ごく簡単にですが、置いてあるリポジトリについて紹介したいと思います。

rl

github.com

レートリミットを実現するHTTPミドルウェアハンドラです。

Goにおけるレートリミット実装としては go-chi/httprate が有名だと思います。rlもgo-chi/httprateを参考に実装しています。

rlの特徴は interface を満たす実装をつくって注入するだけで

  • http.Requestの情報をもとに異なるレートリミットのルールを動的に適用できる
  • 複数のレートリミットのルールを動的に重ねがけできる

というものです。

「あるホストへのアクセスだけレートリミットを緩和したい」だとか「あるIPからのアクセスだけレートリミットをかけたい」というユースケースに対応するためのものです。

rp

github.com

リバースプロキシサーバです。HTTPミドルウェアハンドラではないです。

rpの特徴は interface を満たす実装をつくって注入するだけで

  • http.Requestの情報をもとに異なるUpstreamを動的に指定できる
  • tls.ClientHelloInfoの情報をもとに証明書を動的に指定できる

というものです。

マルチテナントなサービスの前段などで必要になる機能を想定しています。

rc

github.com

HTTPキャッシュミドルウェアハンドラです。

rpの特徴は interface を満たす実装をつくって注入するだけで

  • RFC 9111にしたがってキャッシュを使用してくれる

というものです。また、

  • キャッシュルールの拡張や切り替えも可能
  • http.Requestの情報をもとに異なるキャッシュルールを適用する

ということも可能です。

rcutil

github.com

rcのためのユーティリティパッケージです。

rc.Cacher の interface を満たしたディスクキャッシュ機能の提供などをしています。

mm

github.com

HTTPミドルウェアハンドラを管理するHTTPミドルウェアハンドラです。

mmの特徴は interface を満たす実装をつくって注入するだけで

  • http.Requestの情報をもとに異なるHTTPミドルウェアハンドラを動的に指定できる

というものです。

動的に切り替える仕組みがないHTTPミドルウェアハンドラに動的切り替え機能を付与したいときに使うイメージです。

ちなみに、すごく短いコードなので全部貼り付けておきます。

package mm

import (
    "net/http"
)

// Builder is a middleware builder.
type Builder interface { //nostyle:ifacenames
    Middleware(req *http.Request) (func(next http.Handler) http.Handler, bool)
}

type mMw struct {
    builders []Builder
}

func newMMw(builders []Builder) *mMw {
    return &mMw{builders: builders}
}

func (mm *mMw) Handler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        for _, b := range mm.builders {
            mw, ok := b.Middleware(r)
            if !ok {
                continue
            }
            next = mw(next)
        }
        next.ServeHTTP(w, r)
    })
}

// New returns a new middleware-middleware.
func New(builders ...Builder) func(next http.Handler) http.Handler {
    mm := newMMw(builders)
    return mm.Handler
}

以上、と思ったら、先ほど rlutils というパッケージが誕生していました。

github.com

2manymws*2、どうぞよろしくお願いします。

*1:わかる人にはわかると思います。是非間近で見てほしいので、こちらから応募してください。https://recruit.pepabo.com/

*2:名前の由来は2文字リポジトリが多かったことと、Too manyなミドルウェアを置く場所ということと、例のあれです

カジュアルに使えるスタブサーバとしてhttpstubとgrpcstubを作って使っている

このエントリは Go Advent Calendar 2023 12 日目の記事です。

Goのテスティングパッケージで一番好きなパッケージは net/http/httptest です。

テスト実行時に実際にHTTPサーバを立ててHTTPリクエストを受けるというシンプルかつ強力なアプローチが良いです。

クライアント側にエンドポイントを変える仕組みさえあればクライアントのリクエストを受け付ける形でテストを構築することができるので、選択肢に入れておきたいテスト構成です。

ところで、私たちは runn (ランエヌ)というシナリオテスティングツールを開発しています。

github.com

runnはHTTPクライアントでありgRPCクライアントでもあるのですが*1、そのrunn自体のテストのためにhttpstubとgrpcstubを作って使用しています。

httpstub

github.com

httpstubはHTTPスタブサーバを立てるテストヘルパーです。

一番簡単な使い方としては httpstub.NewServer(t) でスタブサーバを立てた後、メソッドチェインで必要な分だけ各パスのレスポンスを定義してあげたら出来上がりです。

package myapp

import (
    "io"
    "net/http"
    "testing"

    "github.com/k1LoW/httpstub"
)

func TestGet(t *testing.T) {
    ts := httpstub.NewServer(t)
    t.Cleanup(func() {
        ts.Close()
    })
    ts.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)

    res, err := http.Get(ts.URL + "/api/v1/users/1")
    if err != nil {
        t.Fatal(err)
    }
    t.Cleanup(func() {
        res.Body.Close()
    })
    body, err := io.ReadAll(res.Body)
    if err != nil {
        t.Fatal(err)
    }
    got := string(body)
    want := `{"name":"alice"}`
    if got != want {
        t.Errorf("got %v\nwant %v", got, want)
    }
    if len(ts.Requests()) != 1 {
        t.Errorf("got %v\nwant %v", len(ts.Requests()), 1)
    }
}

grpcstub

github.com

grpcstubはgRPCスタブサーバを立てるテストヘルパーです。

一番簡単な使い方としては .protoファイルを指定して grpcstub.NewServer(t, "protobuf/proto/*.proto") でスタブサーバを立てた後、メソッドチェインで必要な分だけ各パスのレスポンスを定義してあげたら出来上がりです。 そう、ほぼhttpstubと同じです。

package myapp

import (
    "io"
    "net/http"
    "testing"

    "github.com/k1LoW/grpcstub"
    "github.com/k1LoW/myapp/protobuf/gen/go/routeguide"
)

func TestClient(t *testing.T) {
    ctx := context.Background()
    ts := grpcstub.NewServer(t, "protobuf/proto/*.proto")
    t.Cleanup(func() {
        ts.Close()
    })
    ts.Method("GetFeature").Response(&routeguite.Feature{
        Name: "hello",
        Location: &routeguide.Point{
            Latitude:  10,
            Longitude: 13,
        },
    })
    // OR
    // ts.Method("GetFeature").Response(map[string]any{"name": "hello", "location": map[string]any{"latitude": 10, "longitude": 13}})

    client := routeguide.NewRouteGuideClient(ts.Conn())
    if _, err := client.GetFeature(ctx, &routeguide.Point{
        Latitude:  10,
        Longitude: 13,
    }); err != nil {
        t.Fatal(err)
    }
    {
        got := len(ts.Requests())
        if want := 1; got != want {
            t.Errorf("got %v\nwant %v", got, want)
        }
    }
    req := ts.Requests()[0]
    {
        got := int32(req.Message["longitude"].(float64))
        if want := int32(13); got != want {
            t.Errorf("got %v\nwant %v", got, want)
        }
    }
}

その他細かい機能とまとめ

httpstubもgrpcstubも基本は指定された通りにレスポンスを返すスタブなわけですが、他にも細かい機能があります。

  • 受け取ったレスポンスを確認する機能
  • リクエストやレスポンスが仕様に沿っているかバリデーションする機能
  • 動的にレスポンスを返す機能
  • OpenAPI Specの examples: セクションの値を使ってレスポンスを返す機能

など、便利に使えています。

実はギョームでもガッツリ使っているので、生じたニーズに応じて都度機能を追加していっています。

httpstubとgrpcstubの使い勝手がほぼ同じなのも便利です。

こういうテストヘルパーを作れるのも、カジュアルにgoroutineでサーバを立てることができるGoの強みの1つですね。

*1:他にもDBクライアントでありCDPクライアントであり任意のコマンド実行ツールでもあります

Go Conference mini 2023 Winter IN KYOTOに参加してきた #kyotogo

京都に行ってきました。

Go Conference mini 2023 Winter IN KYOTO

kyotogo.connpass.com

すでに、ne-sachirouさんによりスライド資料などが集められています。感謝!

scrapbox.io

「miniとは?」となるGoで濃縮された1日になりました。運営の皆様、本当にありがとうございました!

私はと言うと、登壇はしたものの完全にカンファレンスを楽しんでいました。

本編

以下、一言感想です。

Deep dive into log/slog package

既に使ってはいましたが、実装は特にみていませんでした。普通に使えるイメージ。

「nAttrInline = 5」の理由がなるほどなあという理由でした。これ再度計測してレポートしたら変わってくるような値なんですかねー。

Goプログラムがビルドされるまで(コンパイラーの仕組みを探る)

まさにタイトルの通りで、非常にわかりやすい丁寧で面白い発表でした。コンパイラがわかった気になってしまうくらいのわかりやすい発表である意味注意しないといけない発表でした。

Goにおけるcall graphを用いた大規模コードベースの影響調査

私の発表前で、残念ながらリアルタイムでは聴講できませんでした。XのTLがざわついていたのが印象的でした。

あとで資料を拝見させてもらったのですが、影響調査のためのコールグラフを使うというアプローチはなかなかな腕力だなと思いました。すごい。

最近PHP界隈でも「ASTが同じならOK」みたいな腕力が流行っている?ようですけどなんかブームですかね?

Parsing case study in Go

私の発表です。

speakerdeck.com

公開できる事例の多さでは自信があったので(OSSなのでそれはそう)、とにかく事例を詰め込みました。

何かしら持ち帰ってもらえれば幸いです。

日時処理の新スタンダード: Synchro によるタイムゾーン安全、楽々開発

Xで開発しているときから知っていたのですが、とても良いパッケージだと思いました。

じゃあこれを明日からプロダクションに採用するためにはあと何の検討が必要かなーと考えていました。

Go1.22で導入予定だったzeroというbuilt-inについての紹介

大どんでん返しと貴重な経験のお話で面白かったです。

Go Getでのchecksum不一致に遭遇した話とその対応

これは...逆にやらかしたことがあるので、逆にすみませんと言う気持ちになっていました。

CUE+Goで安全かつ簡単に設定ファイルを自動生成してみた

CUEもずっといつか必要になるかも!?と思っているのですが、普段は360YAMLからは一桁二桁小さい数しか扱わないので、いつかのために今後もウォッチしておこうと思います。

context.WithoutCancelについて語る

「伝搬してきた値は使いたいがキャンセルされたくない」というニーズは分かっていたのですが、しっくりきていませんでした。しかし、「contextは値を伝搬する要件とキャンセルの要件の2つを担っていて、現状それを分けることが難しい」という説明にしっくりきました!

Golangを使ったDB用負荷テストツールの開発

流石の tools でした。コードも読んでみようと思いました。あとご挨拶できなかったのが残念(失敗の1つ)。

インターフェースのラッパーを作る際の落とし穴

ラップすると型アサーションに失敗する挙動については知らなかったです。全部作るのは確かに面倒そうだなと思いました。

生成AIによる静的解析ツールの自動生成

静的解析に求められる正しさと生成AIがもたらすまだ不安定な出力の相性って悪いんじゃないかなと思っていたのですが、 確かに https://github.com/gostaticanalysis/skeleton にあるようにある程度テンプレート化されているコードの穴埋めという意味だと「いけそう」と思いました。

The Future of encoding/json

「encoding/jsonJSONエンコードしてくれればいいんじゃ?」と思っていたのですが、この発表で「なるほど良い」と思いました。便利パッケージが1つ増えるのを待つ気持ちです。

続) TinyGo で作る自作キーボードの世界

びっくりした!あんなに簡単にキーボードできるのか!!!!!!!!!

触って理解するGoコンパイラの最適化

感想はこちらになります。

開発チーム横断タスクフォース「Goサブ会」の運用事例と今後の展望

ペパボでもチャプターやGoの情報共有会はあるのですが、Goサブ会はまた違うものでした。 懇親会でも詳しく聞いたのですが、雑な理解で言うと「権限と責任がある」横断組織となっているようでした。

そういう組織がプログラミング言語のくくりであることが「強い!」と思いました。

プロダクションで使うGo Pluginの利便性とパフォーマンス性

プラグインとは」からはじまりGoでできるプラグインのアプローチの紹介でした。

公式見解キツいw

ちなみに私は git-style が好きです。

Revolutionising Inheritance with Generics: A Fresh Approach in Go

Genericsの効果的な活用のお話でした。時間切れだったのであとで資料を拝見しましたが、Genericsを使って親structをつくりつつその親の構造体の1フィールドに子を渡しておくというのはなるほどと思いました。 Embeddingとちょうど依存が逆になっているから「責任の逆転」と言っているという理解でした。

net/http から net.Conn を掘り起こす

なんかreflectパッケージとかを使って引っ張り出すのかな?と思ったらちゃんと正攻法で取得できていました。 知らなかったので知識として覚えておこうと思いました。

GoとテストとインプロセスDB

テストでインプロセスDBを使う話。httptestスキーとしては興味津々で聞きました。

使うためにバグを薙ぎ倒していくのが印象的でした。

個人的な使い方としては syumai さんのポストの方法がよいなあと思いました。

ブログのリアクションから始めるGoのパフォーマンス分析入門

カッコ良すぎるムーブでした。見習いたい。

GoのProtocプラグインを活用した効率的なgRPC負荷試験戦略

.protoファイルからの生成、良いアプローチだと思っていて、私ももっとプラグイン書いて生成していかないとなあと思いました。

作ってよかったgraceful shutdownライブラリ

「Hatenaの知見」「Goサブ会の知見」となっているのが最高だと思いました。

runn開発者会議 in 鴨川

runnの開発者が揃ったので恒例のオフラインでの開発者会議をしていました。懇親会までの時間、鴨川で。

今回出た機能検討は覚えている限りだと次のような感じ

  1. ファイルの存在有無などで実行するステップを変えたい
    • ビルトイン関数への実装以外で何か良いアイデアはないか
    • Fileランナーを作っていいのだろうか
  2. 複数のAPIサーバへリクエストをするようなシナリオで、一部のAPIサーバだけモックしたい
    • 同じシナリオを使ってそれぞれのAPIサーバ開発でテストを行うイメージ
    • https://github.com/vcr/vcr 的な挙動が必要?
  3. 成功したステップをスキップしつつ成功時の値はキャッシュとして利用したい
    • ユースケースとしては2のほうと同じなので、あくまで実現方法の違い
  4. APIテストを担うことが多いが、テストランナーの現状のワンライナーしか許容しない記法があまり可読性良くない
    • 別の記法「も」サポートしたほうが良さそう

どれも実装にいたるまでは詰められなかったので、一旦寝かせてあとはオンラインで引き続き。難しい。。。

懇親会

Gopherのたしなみとして「ORMは何を...?」などから始めつつ、いろいろお話ししました。話が楽しかったのですが、一方でもっといろんな人に挨拶すればよかったなあなどと思いました。

あと、これは課題です。

あとはtenntennさんにPull Requestのレビューを直接お願いするというオフラインカンファレンスムーブをキメるなどしていました*1

2次会

はてな社さんにお邪魔させていただきました。ただ、まあ、もうダメでした。楽しかったです。

感想

最初にも書きましたが本当に濃いGoな1日でした。

お疲れ様でした! ありがとうございました!

*1:後日レビューしていただきました