| name | understanding-isunarabe-domain |
| description | ISUNARABE 合同演習 2026 (nrb2026) アプリケーションのドメイン知識リファレンス。クラウドファンディング型グループ購入「キャンペーン」を中心に、ユーザー・参加・課金・タグ保存検索・Webhook 通知の不変条件と整合性ルールをまとめる。Use this skill when reading or modifying the contest webapp (Rust 参考実装 `migrate/rust/`、Go 移植先、SQL スキーマ)、エンドポイント挙動を理解する必要があるとき、ベンチマーカーが見るクリティカル/ソフトエラー判定を踏まえてチューニングするとき、移植や最適化で API 等価性を確認するとき。 |
Understanding the isunarabe-2026 (nrb2026) Domain
ISUCON 合同演習 2026 課題 isunarabe-train-2026 (DB 名 nrb2026) のアプリケーションドメインを記述したリファレンス。
Use this skill when:
- 参考実装 (
migrate/rust/src/main.rs) や Go 移植 (migrate/go/) を読むとき
- 個別エンドポイントの仕様を確認したいとき
- スキーマ変更・インデックス追加・キャッシュ層導入の影響範囲を判断するとき
- ベンチマーカーが見る不変条件 (二重課金禁止・通知重複禁止・与信整合) を踏まえて挙動を変えたいとき
このスキルは「ドメインの正解」を集めたもの。実装の細かい移植チェックリストは別途 migrate/docs/endpoints/*.md に揃っている。
Quick Domain Map
クラウドファンディング型グループ購入。各キャンペーンには goal_count 人の参加目標があり、参加者数が目標に到達した瞬間に「約定 (closed)」して全参加者へ一斉課金が走る。
| エンティティ | 役割 | 物理表 |
|---|
| User | 購入者。credit_limit (デフォルト 60000) の与信枠を持つ | users |
| Campaign | グループ購入の募集。price 単価 × goal_count 人で約定 | campaigns (画像は LONGBLOB 同梱) |
| Participant | キャンペーン参加レコード。(campaign_id, user_id) 一意 | campaign_participants |
| Charge | 約定時に participant 1 件ごとに 1 件発行される課金 | charges |
| Tag | 公開マスタ (master data)。campaign / saved_search の AND 検索キー | tags (name UNIQUE) |
| CampaignTag | campaign ↔ tag の M:N | campaign_tags |
| SavedSearch | ユーザーごとの保存タグ検索条件。最大 10 件 / ユーザー、各 1..=3 タグ | saved_searches + saved_search_tags |
| AppConfig | KV ストア。現状 notification_webhook_url のみ | app_config |
詳細は references/DATA-MODEL.md。
Critical Invariants — 壊すと即 FAIL
ベンチマーカーがクリティカルエラーで弾く不変条件。1 件で即 0 点。
- 二重課金禁止 — 1 つの
campaign_participant に対して charges 行は高々 1 件。charges.campaign_participant_id は 意図的に UNIQUE が無い ので、再実行・リトライでの二重発行に注意。
- 通知重複禁止 — 同一
(user_id, campaign_id) 組への campaign_closing_soon Webhook は高々 1 回。
- goal 超過参加禁止 —
current_count > goal_count を観測されたら FAIL。participants テーブルの実数で検証される。
- 約定後の課金漏れ禁止 —
status == "closed" のキャンペーンの全参加者は charges を持つ。
- 与信枠超過禁止 —
GET /api/me のレスポンスで credit_used > credit_limit が観測されたら FAIL。
- 与信契約違反禁止 — 与信不足で 200、または与信が足りるのに 402 を返したら FAIL。
- 画像不一致禁止 —
GET /api/campaigns/:id/image のバイト列は POST 時の bytes と完全一致。
詳細・ソフトエラー・スコア式は references/SCORING.md、判定ロジックの根拠は references/BENCHMARK.md。
Endpoints At a Glance
12 本 + healthz。詳細は references/ENDPOINTS.md。
| # | Method | Path | Auth | 備考 |
|---|
| 1 | GET | /healthz | - | DB 失敗時 503 (500 ではない) |
| 2 | POST | /api/initialize | - | DROP→CREATE→seed→app_config UPSERT。30s タイムアウト |
| 3 | POST | /api/users | - | 200 OK (201 ではない)、credit_limit=60000 固定 |
| 4 | GET | /api/tags | - | top-level []string |
| 5 | GET | /api/me | ✓ | credit_used を「未約定キャンペーンの price 合計」として N+1 で派生計算 |
| 6 | GET | /api/campaigns | ✓ | tags AND filter / sort=new|active / 30 件切り |
| 7 | POST | /api/campaigns | ✓ | 201 Created。validation 順番が観測されるので順序保存 |
| 8 | GET | /api/campaigns/:id | ✓ | CampaignRes |
| 9 | GET | /api/campaigns/:id/image | ✓ | image/jpeg + ETag (sha256 hex)。304 を返さない |
| 10 | POST | /api/campaigns/:id/join | ✓ | 与信検査 → 参加 INSERT → (約定なら) charges fan-out → (1人手前なら) Webhook |
| 11 | POST | /api/saved_searches | ✓ | 1..=3 tags / ユーザー上限 10、本文空の 201 |
| 12 | GET | /api/charges | ✓ | top-level [ChargeRes]、amount == campaign.price 重複出力 |
Hard Conventions (移植・改修共通)
- エラーレスポンスは body 空。
{"error":"..."} JSON 包みは禁止 (Rust の AppError::IntoResponse が (status, "") を返す)。
- JSON キーは snake_case。
rename_all 無し → 構造体フィールド名 = JSON キー (goal_count, current_count, last_joined_at, user_id, joined_at)。
- 日時フォーマットは
%Y-%m-%dT%H:%M:%S%.3fZ (UTC, ミリ秒 3 桁固定, リテラル Z)。Go 移植では time.Format("2006-01-02T15:04:05.000Z")。RFC3339Nano は不可。
current_count / status / last_joined_at はカラムを持たない派生計算。participants 配列から導出する規約。
- 画像本体は MySQL
LONGBLOB。S3 等の外部ストレージへの移行はベンチ走行中の外部依存禁止に抵触し得るので注意。
- DB スキーマ・初期データは変更可 (ただし
POST /api/initialize で復帰可能な範囲に限る)。
- 二次 index 無し / FK 無し はチューニング余地として配布されている (
tags.name UNIQUE のみ例外)。
isuwari / /opt/isuwari / isuadmin ユーザー / フロントエンド静的ファイル は変更禁止 (失格)。
ビジネスルールの細部は references/BUSINESS-RULES.md。
When in Doubt
- 表面挙動 (HTTP status, body shape, ヘッダ) は Rust 参考実装が正解。
migrate/rust/src/main.rs を grep する。
- エンドポイント単位の細部 (validation 順、SQL の verbatim 文字列、ロック順序) は
migrate/docs/endpoints/*.md に網羅されている。
- 「ベンチマーカーがどこを見るか」は references/SCORING.md と references/BENCHMARK.md。
- 移植時の I/O 等価性チェックリストは
migrate/docs/endpoints/<slug>.md 末尾の "Porting Checklist" を参照。
Support Files