Gマイナー志向

とくに意味はありません

DuckDB+Evidenceを駆使してISUCON14で21位になった話

2024年12月8日に開催されたISUCON14に「ウー馬場ーイーツ・ザ・ファイナル」として参加しました。最終スコアは29,386でした。実装言語はGoです。

こんなPostをしてましたがRustは採用しませんでした(今回は余裕がなかった)。

今年のチャレンジについて

我らがチームはISUCON開催のたびに新たなチャレンジ*1をしてるのですが、今回は以下の4つにチャレンジしました。

  • ログ解析の見直し
  • 生成AIの導入
  • rustlsの導入
  • bpftuneの導入

ログ解析の見直し ◎

昨年はnginxのアクセスログ解析にkataribeMySQLのスロークエリーログ解析にgo-mysql-query-digestを使っていたものの、これらのツールの出力はテキスト形式が故に出力結果のソートやフィルタリングがしにくいという課題がありました。

いくつかの方法やツールを比較検討した結果、以下のような形になりました。

用途 ツール
nginxのログをPatternも含めてjsonで出力 matsuu/middleware-requestpattern + njs
MySQLのperformance_schemaを抽出 matsuu/slowquery2tsv
nginxとMySQLのログを集計 DuckDB
集計結果のレポート生成 Evidence

nginxのアクセスログはnjsを使わなくてもjsonで出力することは可能なものの、今回njsを使ったのはすべてのリクエストヘッダー、レスポンスヘッダーをログに出力するためです。 kataribeやalpでよくやるパスパラメータ集約はアプリ側でレスポンスヘッダー X-Request-Pattern にパスパラメータを出力することで集約しやすくしています*2

EvidenceはBI as Codeを謳うツールで、様々な情報ソースをSQLで集計してMarkdownベースのテンプレートを用いてグラフなどを含めたレポートを出力することができます。実際の出力結果は以下の通り。

MySQLのperformance_schemaはTSV形式で出力して取り込んでますが、Searchで特定テーブル絞り込みやカラム名選択でソート条件変更が可能です。

MySQLのスロークエリー集計

nginxのアクセスログも同様にSearchやソートが可能です。また複数の観点(右側)で集計を行っています。

nginxのアクセスログ集計

プルダウンメニューで過去のベンチ結果を参照できるようにしています。

プルダウンメニューで過去の結果も閲覧可能

テーブルをメインに一部でグラフを描画していますが、用意されているグラフのパターンもカスタマイズ性も豊富です。Evidenceを実際に試してわかったのですが、これは謳う通りBIツールとしての面もあるものの、静的サイトジェネレータのように動作します。データを基にした定期レポートのような用途にうってつけだと感じました。

ログを起点とした流れはこんな感じです。nginxはnjsを用いてjson形式のログを吐き、MySQLperformance_schema からスロークエリーを取得する形となりました。

graph LR
  subgraph NGINX
    njs
  end

  njs-- access.json -->DuckDB

  subgraph MySQL
    performance_schema;
  end

  performance_schema-->slowquery2tsv
  slowquery2tsv-- TSV -->DuckDB

  subgraph Evidence
    isucon14.duckdb
    Markdown
    Parquet
  end

  DuckDB-->isucon14.duckdb
  isucon14.duckdb-- SQL -->Parquet

  subgraph matsuu.evidence.net
    HTML
    duckdb-wasm
  end

  Markdown-->HTML
  Parquet-->duckdb-wasm

上記EvidenceにはCloudflare Pagesを利用していますが、 npm run build ができる環境があればいいのでセルフホストももちろん可能です。

このような流れを作成した結果、パスパラメータの集約にかかる時間を削減でき、レポートとしても見やすく、かつ情報量も多く盛り込むことができました。やったね。

生成AIの導入 〇

生成AIはGitHub Copilotの導入によるコード補完にとどめました。自分自身にプロンプト力はないので質問を投げるなどはチームメンバーに任せて口頭でその結果を教えてもらっていた形になります。 VSCodeの導入も検討したのですが、結局ターミナル操作もそれなりにあることを考えるとVimが使いやすいだろうとの判断からgithub/copilot.vimを導入しました。

rustlsの導入 ×

事前の検証でrustls-openssl-compatを導入するだけでnginxが高速になることがわかっていたので導入する方向で準備を進めていたのですが、njsを導入するために併せてインストールしたnginx公式リポジトリのnginxではうまく動かなかったようで残念ながら導入できませんでした。こちらは引き続き検証を行う予定です。

bpftuneの導入 ×

BPFを使ってカーネル周りを自動チューニングするbpftuneを導入したものの、検証を行う時間がなかったこともありインストールを行っただけで実際に効果があったかについては確認することができませんでした。こちらも引き続き検証を行う予定です。

体制

あいこん なまえ やくわり ペアプロ
matsuu バリバリ実装する前衛 ドライバー
netmarkjp 司令塔+ベンチ実行+結果解析 ナビゲーター
ishikawa84g セキュリティ+情報官+動作確認
AppArmor、マニュアルや公式アナウンスの把握、ブラウザでの挙動確認
ナビゲーター

3人はそれぞれリモートで、Discordでボイスチャット+画面共有で進めました。

最終構成

サーバ 構成要素 備考
1台目 nginx + app appはGo実装
2台目 MySQL
3台目 使用せず

最後までMySQLボトルネックであったこと、DB分割などもすぐにはできなさそうだったので3台目は活用できませんでした。

スコア推移

Evidenceのスコア記録から

スコア推移

15:34に伸びているのはマッチングの高速化実装、17:11に伸びてるのはMySQLを2台目サーバに持って行ったタイミングです。

やったこと

やったことはコミット履歴がおおよそ参考になるかと思います。 また、8時間の一部始終を録画してYouTubeにアップロードしていますので、興味があればご覧ください。

youtu.be

21位入賞できた理由の考察

今回自分たちのチームが21位と健闘できた理由は以下の2点ではないかと考えています。

多角的な視点を持ってドキュメントやシナリオをきちんと読み解く

ベンチマークを実行したときのStdoutを見ると、シナリオに沿った満足度がログに記録されておりました。これを改善することがスコアを伸ばす近道なのではと考えてマッチング周りの改善に取り組んだのが結果として大きくスコアが伸びる要因になりました。多角的な視点、大事ですね。

完璧を目指さない

今回ボトルネックになっていると判断したN+1が2箇所あり、それぞれに取り組んだものの、どちらもN+1を完全に解消することはできませんでした。ただ、そこにあまり深追いせずN+1のままではあるものの以前よりも処理が軽くなるように実装できたのは大きかったのではないかと考えています。完璧を目指して執着していると他の問題点に気づかず時間を浪費していた可能性もあったため、ほどほどの修正に留めておくということができたのが大きかったのではないかと考えています。

おわりに

今回のISUCONはボリュームも多く、やり甲斐がある問題となっているため感想戦を楽しみましょう。ISUCON未参加だった方も感想戦に参加できますよ!

過去問環境も整備済ですので年末年始だけでなく手元でもぜひ挑戦してみてください!

*1:ISUCON4でGoを初めて触り、ISUCON12でRustでやろうとして玉砕

*2:今回echo実装を用意していたもののchiが使われておりmiddleware-requestpatternはそのまま適用できなかったが手動でレスポンスヘッダーを設定することで運用でカバー