プログラミング

メルカリインターンでN+1クエリを検出するツールを作った

カレー

株式会社メルカリ さんの「Online Summer Internship for Gophers 2020」に参加してきました!

インターンの詳細はこちら

今回作成したN+1クエリを検出するツール -> go_one

概要

2日間講義・3日間開発というスケジュールでした。

静的解析とは

プログラムを実行せずにソースコードの構造や意味をチェックするものです。lint系ツールやコードフォーマッタなどが有名ですね

講義

抽象構文木の解析・型チェック・静的単一代入形式への変換までを演習を交えて教えていただきました。今まで静的解析に触れたことが無かったので難しかったですがgoでは標準パッケージで静的解析を便利にしてくれるものがあるのでやり易かったです。

開発

講義で学んだことを元に自分で作りたいと思ったツールを作成していきました。ISUCONの練習をしているときにN+1を目grepで探すのはめんどくさいので自動で教えてくれるツールが欲しいな〜と思っていました。そこで、N+1を検出してくれる「go_one」というツールを作成しました。(N+1 の 1(one)から来ています)

go_oneとは

大まかには上で述べたとおりN+1を検出してくれるツールです。具体的にはGO言語のfor文の中でDBと通信する型(*database/sql.DBなど)が存在すればN+1だと認識しています。

使用方法

インストール

go get github.com/masibw/go_one/cmd/go_one

実行(bash)

go vet -vettool=`which go_one` ./...

例えばこんな感じのコードがあったとします

rows, _ := cnn.Query("SELECT name, job_id FROM persons")

for rows.Next() {
		var person Person
		if err := rows.Scan(&person.Name,&person.JobID); err != nil {
			log.Fatal(err)
		}

		var job Job

        // This is N+1 query
		if err := cnn.QueryRow("SELECT job_id, name FROM Jobs WHERE job_id = ?",person.JobID).Scan(&job.JobID,&job.Name); err != nil { 
			log.Fatal(err)
		}
		fmt.Println(person.Name,job.Name)
	}

personを複数取得してその後に関連づいているjobを1人ずつ取得しちゃっていますね。ここでgo_oneを使うと以下の様に出力されます。

./hoge.go:38:13: this query is called in a loop

できること

  • 同じパッケージ内のクエリを用いる関数をfor文内で呼び出している場合
  • database/sql,sqlx,gorm,gorpを用いている場合

以上の場合は検出できます

できないこと(今の所)

  • 別パッケージ内のクエリを用いる関数をfor文内で呼び出している場合

別パッケージも対応させたいのですがどんなパッケージにユーザーが設定しているかわからないため依存関係全てを追うことになり、非常に負荷のかかる動作になってしまいます。何かいい案があれば教えてください(PRください)

インターンの感想

Goの静的解析にすごく詳しい方に直接講義をしていただけたりツール作成の時にアドバイスをいただける本当に素晴らしい環境でした。(しかもお給金がでる!!)開発中も他のインターン生の方と話しながら進められましたし,わからないところを教えてくれたりもしました!どのツールも本当に便利で是非他の人のツールも #mercari_intern で探して使ってみてください!

メンター・人事の方・インターン生の方,みなさん本当にお世話になりました!とても楽しかったです!

(これは昼食補助で買ってもらった近所のカレー屋さんのランチメニュー)