babarot

Stars and mentions

Last 30 days, Oct 5 – Nov 3

GitHub stars14
mentions5
01234Oct 5Oct 19Nov 3
UTC days · repositories babarot owns3 repositories

Package downloads

No package download history available yet.

Top repositories

babarot’s repositories ranked by pull request authors, as on trending.

Loading

Contributes to

Repositories outside babarot’s account where babarot opened or reviewed pull requests in the past 365 days.

Who noticed babarot

Everyone who starred or forked babarot’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

5posts
Nov 40 posts
  1. ADRをregoでlintする AIが書くADRのフォーマットをどう揃えるか 自分のリポジトリでは、設計の決定を ADR(Architecture Decision Records)として docs/adr/ に残している。最近は、その ADR の多くを Claude Code に書いてもらっている。決定を相談して、「これを ADR にしておいて」と頼むと書いてくれる。 ただ、AI に書かせた ADR は、少しずつ自由になっていく。テンプレートを置いて書き方を指示していても、ずれるときはずれる。たとえば、 Decision に、画面のメニュー項目の一覧や定数、構造体の定義まで書いてある。仕様書に近づいて、コードを変えるたびに古くなる Status が Proposed のまま、実装は別の形で進んでいる 決定を変えたときに、古い ADR を書き換えたり、新しい ADR が古い ADR の内容を何の印もなく覆したりしている みたいなこと。自分の ADR を持つリポジトリ7つを読み直したところ、コードとずれていたのはどれも、ADR に書かなくてよい種類の内容だった。決定そのものや、守るべき保証は古くなっていなかった。 このうち2つ目と3つ目は、Status の値や ADR 同士の参照といった構造の問題で、見ればすぐに分かる。厄介なのは1つ目で、節も Status も正しいのに、意味のうえでは仕様書になっている。構造を見ても分からず、読んで意味を判断するしかない。 なので、ADR に何を書き、何を書かないかをかっちり決めて、構造だけでなく意味についても守らせたいと思った。 ガイドラインではなくポリシーで縛りたい AI が書く ADR の質をどう保つかは、ほかの会社でも話題になっている。 以下の記事は、雛形だけで AI に ADR を書かせると読みづらい ADR が頻出したので、過去のレビューの指摘から規約をまとめたガイドラインを作った話。ガイドラインを、ADR を書くスキル、Claude Code の rule、CI の AI レビューから参照させている。記載ルールがレビュー用のプロンプトや過去の指摘に分散していた、という問題意識もおもしろかった。 zenn.dev 以下の記事は、ADR を書く Claude Code の skill を用意し、テンプレートとガイドラインに沿って書かせている。 zenn.dev どちらも参考になるが、ガイドラインやスキルは結局のところ AI へのお願いになる。自分もルールを文章にして、Claude Code が ADR を触るときに読む rule に置いているが、守られることが多くても、守られる保証はない。AI レビューで指摘させるのも、指摘するのがまた LLM なので、見逃すこともあれば、同じ ADR に違うことを言うこともある。 そこで、ルールをガイドラインではなくポリシーとして書くことにした。以下では、ADR が守るべき決まりを「ルール」、それを rego で書いて検査できるようにしたものを「ポリシー」と呼ぶ。 構造のルール(節の有無と順序、Status の値、ADR 同士の参照、リンク先が存在するか)は、ポリシーで機械的に判定し、違反があれば CI を落とす 意味のルール(仕様書になっていないか)は、ポリシーの中から LLM に質問して判定させ、結果を PR に注意として出す 意味の判定には LLM が要るが、何を判定させるかはポリシーに書いて、構造のルールと一緒に管理する。LLM の答えはぶれるので CI は落とさず、注意に留める。こうすれば、書き手が人でも AI でも、構造は同じ基準で弾き、意味のずれも PR の上で気づける。 ルールをregoで書くためにCLIを作った conftest(OPA)は、構造化されたデータをポリシーで検査するためのツールで、こういう検査はまさに専門なので、lint のルールは独自の DSL や YAML の設定で書くより、rego で書くのがよいと思っている。 ポリシーがコードなので、opa test でテストを書ける。条件の組み合わせを設定のキーで表すより、素直に書ける 「置き換えの参照が循環していないか」のような、集合やグラフの規則が書きやすい 実際、前に書いた conftestでMarkdownの構造をバリデーションする でも、remark と conftest で Markdown の構造を検査していた。 ただし conftest は Markdown を読めない。前の記事では、remark で AST の JSON にし、jq でファイル名を足してから conftest に渡すシェルスクリプトを書いていた。このつなぎの部分が弱く、変換に失敗したファイルを見逃して、残りのファイルだけで検査が通ってしまうことがあった。リンク先が存在するかのように、ファイルを見ないとわからないことも、rego からは調べられない。 なので、Markdown の解析と OPA での評価を1つのバイナリにまとめた CLI を作った。 babarot/regomark regomark がやっていることは次のとおり。 Markdown を解析し、最上位のブロックとリンクを行番号付きの JSON にする リンクをリポジトリに対して解決し、リンク先が存在するかを JSON に入れる ポリシーが意味について質問していれば、その質問と対象の原文を LLM に渡して答えをもらう 組み込んだ OPA で、rego のポリシーを評価する。構造の検査だけでなく、LLM の答えを使って ADR の内容をルールと照らし合わせる意味の検査もできる(後述) 違反を行番号付きで出し、deny があれば終了コードで CI を落とす regomark 自体には ADR についての実装はなく、ADR のルールも、LLM に何を判定させるかも、すべて rego のポリシーの側にある。解析から評価までを1つのバイナリで行うので、Node の依存も、conftest につなぐシェルスクリプトも要らなくなった。 ルールを構造と意味に分ける ADR の書き方のルールは、まず文章として .claude/rules/adr.md にまとめ、paths: docs/adr/** で Claude Code が ADR を触るときに読ませている。そのうえで、各項目を rego のポリシーにして regomark で検査している。 ルールの項目と、検査のしかたの対応はこうなっている。 ルール 種類 検査のしかた ADR は意思決定の文書で、仕様書ではない。画面の細部、定数、コード片は書かない 意味 仕様書になっていないかを LLM で判定し、warn にする 作業の手順は書かない 意味 手順書になっていないかを LLM で判定し、warn にする 節は Status、Context、Decision、Consequences の4つ 構造 節の有無と順序を deny で検査する Status の値は Accepted、Rejected、Deprecated、Superseded by ADR-NNN のどれか 構造 値を deny で検査する Status の最初の段落には、値と Partly replaced by ADR-NNN の行だけを書く 構造 段落の形を deny で検査する 決定を変えるときは新しい ADR を書き、古い方に Superseded by か Partly replaced by を付ける 構造 参照先が存在するか、向きが正しいか、循環していないかを deny で検査する 決定を守るテストやパッケージは、ファイルへの相対リンクで名指しする 構造 リンク先のファイルが存在するかを deny で検査する 意味のルールは、ADR がその文書としてふさわしいかを問うもので、ふつうは機械的に検出できない。「ADR が仕様書や手順書になっていないか」はいちばん分かりやすい例で、上で紹介した記事のガイドラインでも、Decision が実装の詳細を抱え込む ADR が問題として挙がっている。こうしたルールは LLM を通して判定する。 一方、構造で判定できるように、ルールの側も機械が読める形に寄せた。regomark は Status の最初の段落を値と参照として読み、理由まで同じ段落に書かれるとどこまでが値なのかが決められなくなるので、その段落には値と参照だけを置く。名指しをリンクにするのも同じで、ADR の本文から関数名を推測して探すより、リンク先が存在するかを見るほうが確実に判定できる。 regomarkの使い方 コマンドは3つ。 regomark check [--config .regomark.yaml] [--format text|github|json] [--judge | --no-judge | --judgments FILE] regomark dump # ポリシーに渡す入力の JSON を出す(テストを書くため) regomark questions # ポリシーが LLM に判定させる質問を出す 設定はリポジトリの直下の .regomark.yaml に書く。 documents: docs/adr # 検査する Markdown のディレクトリ policy: .regomark/policy # rego のポリシーのディレクトリ entrypoint: adr # deny と warn を集めるパッケージ(data.adr) skip_rules: [] # 違反を捨てるルール ID exclude: [] # 検査しない文書(docs/adr/000-*.md など) judge: # ポリシーの質問を LLM でどう判定するか(任意) command: [sh, -c, 'claude -p --output-format json --model sonnet --tools "" --json-schema "$REGOMARK_ANSWER_SCHEMA"'] max_questions: 200 # 1回の実行で聞く質問の上限 regomark check は、違反を path:line: [rule] message の形で出す。ルールを決める前に書かれていた gh-infra の ADR に当てると、こうなる。 $ regomark check docs/adr/002-import-action-selector.md:5: [status-value] Status "Proposed" must be Accepted, Rejected, Deprecated or "Superseded by ADR-NNN" docs/adr/003-nullable-deletion-markers.md:1: [required-sections] missing "## Consequences" docs/adr/003-nullable-deletion-markers.md:1: [required-sections] missing "## Decision" docs/adr/004-deletable-deletion-markers.md:1: [required-sections] missing "## Consequences" docs/adr/004-deletable-deletion-markers.md:1: [required-sections] missing "## Decision" $ echo $? 1 --judge を付けると、ポリシーの質問を LLM で判定し、意味の注意も warning: として並ぶ(後述)。--format github にすると、どちらも GitHub Actions の注釈として PR の差分に付く。終了コードは、deny がなければ 0、あれば 1、設定やポリシーの誤りで検査そのものができなければ 2。warn は終了コードを変えない。 構造のルール ポリシーに渡る入力は regomark dump で見られる。goldmark で取り出した最上位のブロックが、文書の順に、始まりと終わりの行番号付きで並ぶ。 $ regomark dump | jq -c '.documents[1].blocks[:4]' [{"kind":"heading","level":1,"line":1,"end_line":1,"text":"ADR-002: Import action selector for interactive diff viewer","lang":""}, {"kind":"heading","level":2,"line":3,"end_line":3,"text":"Status","lang":""}, {"kind":"paragraph","level":0,"line":5,"end_line":5,"text":"Proposed","lang":""}, {"kind":"heading","level":2,"line":7,"end_line":7,"text":"Context","lang":""}] リンクは regomark がリポジトリに対して解決し、kind(file、dir、missing、outside、external)まで入れて渡す。rego はファイルを読めないので、リンク先が存在するかは regomark の側で調べておく。 Status の値を検査するポリシーはこうなっている。 deny contains {"rule": "status-value", "path": doc.path, "line": adrlib.status_paragraph(doc).line, "message": msg} if { some doc in input.documents s := adrlib.status(doc) not s in allowed_status not regex.match(^Superseded by ADR-\d{3}\.?$, s) msg := $"Status \"{s}\" must be Accepted, Rejected, Deprecated or \"Superseded by ADR-NNN\"" } 違反は rule、path、line、message を持つオブジェクトで返す。行番号は入力のブロックから取るので、違反は Status の値が書かれた行に付く。adrlib はポリシー側に置いたライブラリで、「ある見出しの節に含まれるブロック」を返す関数などを持っている。 置き換えの循環は graph.reachable で書ける。 deny contains {"rule": "replacement-cycle", "path": doc.path, "line": r.line, "message": msg} if { msg := "this ADR is in a cycle of replacements" some doc in input.documents some r in adrlib.refs(doc) some d in numbered d.number == r.target doc.path in graph.reachable(replacement_graph, {d.path}) } 構造のルールは今のところ14ある。 ファイル名、H1、必須の節とその順序 Status の形と、置き換えの参照の置き場所 参照先の ADR が存在するか、参照の向きが正しいか 番号の重複、欠番、000 番 置き換えの循環 Accepted の ADR から、リポジトリ内の存在しないファイルへの相対リンク regomark は独自の組み込み関数を足していない。なので、ポリシーは素の opa test、opa check --strict、Regal でそのまま検査できる。 意味のルール ここからが、普通の lint にはできないところ。「この ADR は仕様書になっていないか」「1本の ADR に2つの決定が入っていないか」のような意味の判定は、rego でも Go でも書けない。LLM なら判定できる。 regomark では、ポリシーが LLM に質問できる。何を聞くかも、答えをどう扱うかも、構造のルールと同じ rego で書く。 質問をポリシーに書く ポリシーに questions を書くと、regomark がそれを LLM に判定させる。質問は次の項目を持つオブジェクトで、「どの文書の、どのブロックについて、何を聞くか」を表す。 id: 質問の種類 path と line: 対象の文書と、その場所 blocks: LLM に見せるブロック。入力の blocks から選ぶ ask: yes か no で答えられる質問 たとえば「Decision の節に、関数名や定数が書かれているか」を聞いて、yes なら注意を出すポリシーはこう書ける。 questions contains {"id": "spec-detail", "path": doc.path, "line": h.line, "blocks": [doc.blocks[i + 1]], "ask": "Decision の節に、関数名や定数が書かれているか"} if { some doc in input.documents some i, h in doc.blocks h.kind == "heading" h.text == "Decision" } warn contains {"rule": "spec-detail", "path": j.path, "line": j.evidence_line, "message": j.reason} if { some j in input.judgments j.id == "spec-detail" j.status == "ok" j.verdict == "yes" } regomark は、質問と、選んだブロックの原文を行番号付きで judge.command(上の設定では claude -p)に渡す。LLM は yes か no と、その理由と、根拠にした行番号で答える。yes の根拠は、渡した行のどれかでなければならない。答えは input.judgments としてポリシーに戻り、上の warn が拾う。 手元で regomark check --judge を実行すると、こうなる。 regomark check --judge 最初に挙げた「Decision に定数や構造体の定義まで書いてある」ADR が、こうして根拠の行と理由つきで PR に出る。節も Status も正しいので、構造のルールだけでは通ってしまっていたものだ。 答えには status が付く。答えが返れば ok、聞かなかったら not_run、コマンドが失敗したら provider_failed、答えの形が崩れていたら invalid_answer。聞けなかったときにどうするか(たとえば provider_failed なら注意を出し、not_run なら何も言わない)も、ポリシーで決める。 LLM の答えを読めるのは warn だけで、答えによって CI が落ちることはない(後述)。 ローカルで意味検査をする(claude -p) 意味検査をするかどうかは、使う人が決める。リポジトリの .regomark.yaml に書けるのは「どう判定させるか」(judge.command)までで、実際に意味検査をするのは --judge を付けたときか、REGOMARK_JUDGE=on のときだけ。人のリポジトリで regomark check を打っても、そこに書かれたコマンドが勝手に実行されることはない。 LLM を呼ぶ回数と実行コストにも気を配っている。 答えは、送った中身とコマンドをキーにキャッシュする。ADR とポリシーを変えなければ、2回目からは LLM を呼ばない 1回の実行で聞く質問が max_questions を超えたら、聞かずに止める コマンドは並列で動かし、時間切れを設ける LLM には ADR の本文を渡すので、本文に指示が書かれていればそれに従うかもしれない。なので、LLM にはリポジトリを変更する手段を持たせず、上の設定でも claude -p に --tools "" を付けている。 CIで意味検査をする(claude-code-action) CI では、claude-code-action のように、ワークフローの1つのステップとして動く LLM に答えさせたい。これは質問ごとに呼べるコマンドではないので、質問をまとめてファイルで渡す。 regomark questions で、質問と LLM に渡すプロンプトをファイルに書き出す 別のジョブで、claude-code-action にすべての質問に答えさせ、答えをファイルに書かせる もう1つのジョブで、チェックアウトし直して regomark check --judgments で答えを読む LLM が検査するチェックアウトに触れると、たとえば skip_rules にルールの ID を足して deny を消せてしまうので、LLM は検査と別のジョブで動かす。答えは質問の中身から作ったキーで照らすので、ADR やポリシーが変わったあとの古い答えは、どの質問にも当たらない。fork からの PR のように secrets がなく LLM のジョブが動かないときは、答えなしで構造のルールだけを検査する。ワークフローの全体は README に置いている。 検査の流れ 1回の regomark check は、LLM で判定する段階と、ポリシーを評価する段階に分かれている。 ポリシーの questions を評価して、LLM に判定させる質問を決める。これは意味検査をするかどうかに関係なく、毎回行う 質問に答える。judge.command で LLM に判定させるか、答えのファイルを読むか、何もしない(すべて not_run) ポリシーを2回評価する。deny は LLM の答えなしの入力で、warn は答えありの入力で評価する 上で書いたとおり LLM の答えは毎回同じとは限らず、同じ ADR がある日は通って次の日は落ちる CI にはしたくないので、LLM の答えは warn にしか渡さず、そのために評価を2回に分けている。答えがあるときとないときで deny の結果が変わるポリシー、つまり deny が LLM の答えを読んでいるポリシーは、終了コード 2 で止める。 $ regomark check regomark: data.docs.deny depends on input.judgments: it reports different violations with them (1) than without them (0); judgments may only feed warn これで、CI が落ちるかどうかは構造のルールだけで決まり、意味検査をやめても、LLM が失敗しても変わらない。意味の判定は、PR の上の注意として人が読む。 ポリシーの書き間違いをどう見つけるか lint でいちばん困るのは、違反があるのに 0 で終わること。CI が緑なら、誰もその ADR を見直さない。rego は、書き間違えてもエラーにならずに「何も見つからなかった」になりやすいので、regomark は次のようなポリシーを、終了コード 2 で止めるようにしている。 パッケージ名の誤り: package adr.links と書くと、違反は data.adr.links.deny に入り、誰も集めない。lib/ の外のモジュールは entrypoint のパッケージ(package adr)でなければならない deny の打ち間違い: deny を denies と書いたモジュールは、違反を1つも出さない。lib/ の外のモジュールは deny、warn、questions のどれかを必ず定義する 関数として書いた deny: OPA はパッケージの値に関数を含めないので、評価の結果から消える どのモジュールも定義していない data の参照: rego では未定義になり、それを使う deny や warn が何も出さない 入力の項目名の打ち間違い: 入力は Go の構造体から生成した JSON Schema で、評価の前に検証する skip_rules の打ち間違い: ポリシーに rule_ids で ID を宣言してもらい、それと照らす 形の崩れた質問: 項目が足りない、入力にないブロックを指している、といった質問は、LLM に渡す前に止める どれも rego としては正しいので、opa check は通ってしまう。regomark のテストでは、Markdown から終了コードまでを通し、「違反があるのに 0 で終わらない」ことを確かめている。 ルールとポリシーを各リポジトリに配る ルールの文章、ポリシー、設定、ワークフローは、自作の gh-infra で各リポジトリに配っている。gh-infra は GitHub のリポジトリの設定やファイルを YAML で宣言して管理するツールで、FileSet を使うと、同じファイルを複数のリポジトリに PR として配れる。 rego のポリシーもただのファイルなので、ディレクトリごと FileSet で同期すれば、足したポリシーは各リポジトリに届き、消したポリシーは消える。ルールの文章とポリシーを同じ PR で変えられて、ルールを足すのに regomark のリリースは要らない。ポリシーを書く regomark と、それを配る gh-infra を組み合わせると、複数のリポジトリの ADR を1か所のルールで揃えられる。 gh-infra については以下の記事で紹介している。 zenn.dev CI では、mise でバージョンを固定して実行している。 - name: Check the ADRs run: mise x github:babarot/regomark@0.2.0 -- regomark check --format github env: mise-action はトークンを渡さないので、共有のランナーでは API のレート制限に当たる MISE_GITHUB_TOKEN: ${{ github.token }} ADR の中のリンクは、テストのファイルを改名するような、ADR を触らない PR でも切れるので、ワークフローはパスで絞らず、すべての PR で動かしている。 まとめ ADR のルールは、Claude Code が読む文章と、regomark が検査する rego のポリシーの2つで持つ形になった。構造のずれは deny で CI が落とし、仕様書に近づいていないかのような意味のずれは、ポリシーから LLM に判定させて warn として PR に出す。何を検査するかも、LLM に何を判定させるかも同じ rego に書いて、同じ PR で変えて配れる。 実際に regomark を使っている例として、自分のリポジトリを2つ挙げておく。どちらも CI では mise で regomark を取ってきて、.regomark/policy/ に置いた rego のポリシーで docs/adr/ を検査している。 リポジトリ ワークフロー ポリシー babarot/oksskolten regomark.yaml .regomark/policy babarot/claude-recall regomark.yaml .regomark/policy oksskolten は前の記事のリポジトリで、ADR の検査を conftest から regomark に移した。ポリシーは構造、Status、参照、リンクのファイルに分けていて、それぞれに *_test.rego のテストがある。 regomark は ADR 専用のツールではなく、内部に ADR についての実装もないので、ほかの Markdown の検査にも使えるはず。作ったばかりで、自分のリポジトリで回している程度だが便利だったので、Markdown を rego で検査したい人は試してみてください。 babarot/regomark

  2. ISUCON2026 本番に出た 10/31 の ISUCON2026 本番に出ました。ISUCON は今回が初参加です。結果は 最終スコア __ 点・__ 位 です。どちらもまだ確定していないので、いまは空けておきます。 この記事は、当日の Claude Code の会話ログ(~/.claude/projects/ の JSONL)と git の履歴から Claude Code が時刻を拾い直して書いたものを、自分でこね直したものです。数字や経緯は claude-recall で会話を検索して確かめました。 準備 suburi と template リポジトリ 本番の前に、練習の環境とリポジトリの雛形を用意しました。 練習には自分でバイブコーディングして作った suburi というサイトを使いました。ISUCON の素振りを好きなときに始められるポータルで、private-isu と ISUCON9 予選、ISUCON13に対応しています。GitHub でログインしてチームを作り、出てきた CloudFormation の YAML で自分の AWS に環境を立てて、ポータルからベンチを回します。初参加なので、これを作りながら当日の流れをイメージすることができたのは地味によかったです。 テンプレートリポジトリはこのサイトを使って素振りしながら作りました。 ntk221/isucon-template 前日までの準備が終わった状態のリポジトリで、本番の朝もここから作業リポジトリを作りました。特に珍しいものは入ってないかなと思うのですが、「GitHub からデプロイできるようにする」のと、「ボトルネックを見つけるためのデータをある程度取れるようにする」という2点が事前に用意できてよかったなと思っています。あと、セットアップを雑に Claude に任せられるようにスキルを用意できていたのですが、当初の想定と差分がある点についても Claude が当日うまく対応してセットアップできていたので、よかったと安心しました。 exe.dev の Claude Code をチームで使う Claude Code は手元の PC ではなく exe.dev の VM で 1 つだけ動かしました。メンバーはこの VM に入って同じ Claude Code を使うようにしました。 セッションの記録はすべて同じ VM の ~/.claude/projects/ に残ります。そのため、交代で作業しても、前の人のセッションを見返したり続きから再開したりできます。この記事のタイムラインもその記録から作りました。 お題: ISUREAL 日常の写真を友達と共有する SNS「ISUREAL」でした。投稿期間が始まると通知が飛び、利用者は写真を撮って投稿し、友達の投稿を見て、コメントや顔写真のリアクション(RealMoji)を付けます。 構成でいちばん影響が大きかったのは 地域 です。 台 地域・役割 isu1 asia1: nginx + Go アプリ isu2 europe1: nginx + Go アプリ isu3 america1: nginx + Go アプリ isu4 asia2: PostgreSQL 16(全地域で共有) ルーターが国ごとに振り分け、地域の間には遅延が入っています。DB は asia にしかないので、europe と america のアプリは SQL を 1 本実行するたびに、地域をまたいで往復 します。全台 c5.large(2 vCPU / 3.7GiB)。 結果的に、今日の改善の半分以上は「europe / america から DB への往復を何回減らせるか」でした。 タイムライン 10:00〜18:00 にやったことを時刻順に並べました。長いので畳んであります。 当日のタイムライン(10:00〜18:00) 10:00〜10:45 セットアップと初回ベンチ 10:03 作業用 VM の公開鍵を出してもらい、10:16 には 4 台に ssh が通る状態に 自作の /isucon-setup スキルにサーバの IP と当日マニュアルを渡して、Makefile を書き換えてもらう。テンプレートは「MySQL + nginx 1 台」前提だったので、PostgreSQL・地域ごとの nginx とアプリ 3 台に合わせた 10:32 ポータルで初回ベンチ。make measure で alp・pg_stat_statements・CPU プロファイルを log/ に落とす 10:44 「make show でログを見つつボトルネックを考えて」と頼む isureal-guard.service(ログイン失敗を連打した IP を 2 分 ban する)が動いていることや、/api/initialize は TRUNCATE と DELETE だけでテーブルを作り直さないことを、最初の調査で拾えたのは助かりました。 11:00〜11:45 index を 5 本 最初に効いたのは index でした。画像の取得で「RealMoji の画像か」を確かめる EXISTS が、120 万行ある realmojis を Seq Scan していました。ベンチ 1 回で 682 回、読んだ行は 4.3 億行。画像の取得は平均 552ms で、DB の時間の 1 位でした。 友達関係・友達申請の Seq Scan もまとめて消す index を 5 本足したところ、ベンチのリクエスト数が 4,360 → 8,205(約 1.9 倍) になりました。 12:00〜13:00 細かい往復削減と、OpenTelemetry この時間は細かい改善を並べています。 登録の LOCK TABLE users をやめ、username の UNIQUE と ON CONFLICT で重複を弾く 画像 URL の署名の exp の刻みを 10 秒から 10 分にし、署名のキャッシュが当たるようにする ログインの DB 往復を 7 回から 4 回にする(#12) PostgreSQL の shared_buffers などを調整 並行して、OpenTelemetry の計装を入れました(#2)。make deploy OTEL=1 で計装ありのバイナリを動かし、make measure でトレースも集計するようにしています。europe / america のどのリクエストが何回 DB に往復しているかが、トレースで数えられるようになりました。 12:52 往復を 1 回にまとめたら遅くなった(#13 → revert) フィードなどで閲覧者の情報を取る 5 本の SQL を、1 本にまとめました(#13)。往復は 5 回から 1 回になります。ところがスコアは下がりました。 まとめた 1 本の SQL は 1 回平均 27ms かかり、2,070 回で合計 56 秒になっていました。まとめる前の 5 本はどれも 1 回 0.1ms 程度です。往復は減っても、DB の負荷が大きく増えていました。7 分後の 12:59 に revert しています。 その後 14:00 に、SQL は 5 本のまま pgx の Batch で 1 回の往復で送る 形で入れ直しました(#19)。「往復を減らす」と「SQL を 1 本にまとめる」は別物だ、というのが今日いちばんの学びです。 13:12 のベンチは 13,584 点(コミット 4b7a50f)でした。 13:00〜14:30 地域をまたぐ処理を減らす 13:33 に「マニュアルのスコアリングと measure の結果から、地域をまたぐ部分の改善案を出して」と頼み、ここから issue を立てて PR に分ける流れが定着しました。 画像のバイト列を台ごとにメモリにキャッシュする(#14) 画像 URL の署名で、HMAC の鍵の下準備を 1 回だけにする(#16) 通知の SSE を台ごとに 1 本で待ち、投稿期間を開いた台から kick を送る(#17) 通知の行を投稿期間を開くときに作らず、SSE で返すときに作る(#20) 13:40 には europe と america で投稿の作成(POST /api/posts)に 0.6〜1 秒かかる原因を、トレースから調べてもらいました。 14:04 画像をディスクに置こうとして、やめた image_objects WHERE id = ANY($1) が合計 58 秒かかっていて、isu4(asia2・DB)の iowait が約 26% ありました。画像をアプリの台のディスクにも置けば DB から読まずに済みます。 ただ、初期データの画像を各台に書き出す prefill に 1〜2 時間かかる見込みでした。マニュアルと reset.sql を読み直して、初期化の規約にも引っかからないか確かめたうえで、14:30 にこの方針は閉じました。代わりに、画像の中身を TOAST の圧縮にかけない(STORAGE EXTERNAL、#38)変更を入れています。 ディスクに置く案は最後に「新規アップロードと GET で一度読んだ画像だけをオンデマンドで置く」形(#51)で入りました。 14:30〜16:00 PR を次々に入れる この時間帯はマージの間隔が 10〜20 分でした。 アップロードされた JPEG を全部デコードせず、ヘッダーだけ読んで大きさを確かめる(#23) nginx の接続数の上限を上げ、アプリへの接続を使い回す(#35) フィードの投稿を、種類の絞り込みを SQL に足し、時刻順は 100 件ずつ LIMIT をかけて取る(#40) エクスポートの画像を区分を絞って並行に読む(#41) セッションを台ごとにキャッシュする(#45) #35 はベンチで出ていた POST /api/posts: 通信に失敗しました: readfrom tcp ... や EOF、502 への対応です。asia1 の nginx の worker_connections が既定の 768 のままで、負荷テストの終わりに接続が足りなくなっていました(issue #34)。 14:50 ごろには、open の issue が 15 本ほど並んでいました。issue に書いておけば、別のセッションでも同じ前提で作業を始められます。 16:10 プールを 80 本にしたら雪崩れた(#24 → revert) 16:10 に #24(IP ban の読み直しを裏に回す・RTT 削減・画像のロック)を入れたところ、通知の SSE が 60 秒で切られるようになりました。asia1 の DB 接続プールを 80 本にしていたのが原因で、DB が雪崩れていました。16:24 に revert しています(#49)。 最後の 17:20 過ぎに、通知の SSE と hub の確認を本体とは別の小さな DB プールで流す PR(#54)を出しました。本体のプールの待ちで SSE が 60 秒切られないようにするためです。 16:30〜18:00 最後の追い込み 友達申請の作成と承認・拒否・取り消しを DB の関数にして 1 往復にする(#48) 投稿・プロフィール・関係の更新をまとめ、DB との往復を減らす(#50) 17:00 計測用のもの(pprof の口、nginx の access_log、pg_stat_statements)を剥がす(#52) コメント・RealMoji の送信も DB の関数で 1 往復に(#53) 17:37 /api/initialize の agent・harness・model を、実際に使ったエージェントとモデルに直す(#55) 17:00 にリーダーボードが止まってからも、18:00 まで PR を入れ続けました。 感想 よかったこと 事前に用意したテンプレートを、チームがうまく使ってくれたのが嬉しかった。途中からは、チームで声を掛け合いながら並列に作業できるようになりました。うまく息を合わせて改善を入れられたと思います。 マニュアルやレギュレーションを Claude Code のコンテキストに入れておき、違反がないかを確かめながら進められた 難しかったこと コードベースやサービスについてのメンタルモデルが十分にできていないまま、改善を入れることが多くなってしまった感じがある。どうせ、人間が介在するなら、ここをうまく理解できるようにする工夫を入れたらよかったかもしれない。たとえば、データモデルとか処理の説明を Artifact で作ってみんだでそれをみながら議論するとか リージョンが分かれているという制約に対しても、うまくアプローチする案を試しきれなかった 総じて 自分としては初めての ISUCON で、仲間とコミュニケーションしながら頑張って挑戦できてよかったな、という感想です。 チームは仕事の同僚ですが、同じ案件に入っているわけでもありません。こういう機会で、お互いのことを知れたような気がします。 打ち上げやるぞ!おー! 最後に 運営のみなさん、ありがとうございました!地域ごとの遅延があるお題は、どこで地域をまたいでいるかを数えるところから始まり、最後まで面白かったです。

  3. 手元のリポジトリをGitHubっぽい見た目で開くgh拡張を書いた。hugo serverみたいなイメージ。pushする前に確認したり並行するworktreeと比較したりgit差分も見れて便利 babarot/gh-mini t.co

    34 likes · 4 reposts · 2,352 views
  4. herdrのin-app toastをstackできるようにしてクリックしない限り消えないようにするpatch書いて改造した github.com/babarot/dotfiles/blob/main/nix/home-ma… github.com/babarot/dotfiles/blob/main/nix/home-ma… t.co

    5 likes · 357 views
  5. owner/repo#123みたいなplain textをlinkにするClaude Code Modsつくった babarot/claude-linkify t.co

    3 likes · 1 repost · 924 views

Achievements

Awards earned by the repositories babarot owns. Select an award to embed its badge.

No awards yet

Repositories earn awards by reaching the top three in a completed trending day or week.

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.