db-concurrency について
データベース監査向けのスキルで、リレーショナル、キー・バリュー、ドキュメント、ワイドカラムストアの並行処理リスクを調べます。静的な確認とデータベースクエリを組み合わせ、潜在的なリスクと実行時に確認された競合を区別します。
主な機能
- 01トランザクション分離レベルの確認
- 02危険な読み取り・更新パターンの検出
- 03キューのロックと SKIP LOCKED の確認
- 04冪等キーと条件付き書き込みの確認
- 05トランザクション単位のアドバイザリロックを提案
- 06データベース統計による競合の検証
活用シーン
- 01残高や在庫の更新ロジックをレビュー
- 02データベースワーカーキューのロックを評価
- 03再試行による重複書き込みを確認
- 04実行中のロック待ちやデッドロックを調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ hainrixz/ claude-db --skill db-concurrencyインストール後、Claude Code で/db-concurrencyを入力して読み込まれたか確認します。
詳細
db-concurrency(M14)
同時書き込み時の正確性は、パフォーマンスとスケール(軸 performance)に関わる問題です。スループットと、負荷時にデータの一貫性が保たれるかどうかの両方を左右します。リレーショナルの Concurrencia w12(および KV/ドキュメント/ワイドカラムのプロファイルにおける Idempotencia カテゴリー)に反映されます。
チェック内容
- 分離レベル — ワークロードで選択された分離レベル(デフォルトの
READ COMMITTEDと、REPEATABLE READ/SERIALIZABLEのどちらか)は、その不変条件に適していますか?ロックなしでREAD COMMITTEDを使用する金額/在庫の更新は、異常が発生する可能性があります。 - 更新の消失/読み取り・変更・書き込み —
SELECT balance; …app math…; UPDATE balance=$newを、SELECT … FOR UPDATE、アトミックなUPDATE … SET x = x - $n、または楽観的バージョンチェックなしで実行していませんか?典型的な二重支払いの競合状態です。 - キューの競合 —
SKIP LOCKEDを使わずSELECT … FOR UPDATEを使用するワーカーポーラーでは、すべてのワーカーが1つの行に直列化されます。FOR UPDATE SKIP LOCKEDを推奨します。 - 冪等性(KV/doc/WC) — 非トランザクション型のストア(Redis、DynamoDB、Cassandra、セッションを使用しない Mongo)への書き込みに冪等性キー/条件付き書き込みがないと、再試行で処理が二重適用されます。
- アドバイザリーロック — クリティカルセクションに ロックする行がない 場合は、Postgres のアドバイザリーロックで直列化することを推奨します。リーダー選出、単一実行の cron ジョブ、マイグレーションのガードには、
pg_advisory_xact_lock()(トランザクションスコープで、コミット/ロールバック時に自動解放)を使用します。接続が切れた際にロックが残る可能性があるセッションスコープのpg_advisory_lock()より、_xact_版を優先してください。注記(相互参照 M15): セッションスコープのアドバイザリーロックは、トランザクションモードのプーリング(PgBouncer)を破綻させます。ロックと解除が別のバックエンドに割り当てられる可能性があります。トランザクションスコープのアドバイザリーロックはプーリングに安全です。
スコア/軸
performance のみに反映されます(リレーショナルの Concurrencia w12、KV w18/ドキュメント/ワイドカラムのプロファイルにおける Idempotencia)。
Tier-0(静的)
ORM/トランザクションのソースを grep して、分離レベルの設定、ロックなしの読み取り後書き込み更新シーケンス、SKIP LOCKED のない FOR UPDATE、KV/doc/WC 書き込みにおける条件付き書き込み/冪等性キーの不足を確認します。静的解析での検出結果は directional です。実際の競合は実行時に発生するためです(needs_api/Tier-2)。
Tier-1/2(検証)
デフォルトの分離レベル(Tier-1):SHOW transaction_isolation;(メソッド connection_introspect)。
実際の競合(Tier-2):
SELECT wait_event_type, wait_event, count(*)
FROM pg_stat_activity WHERE state='active' GROUP BY 1,2 ORDER BY 3 DESC;
-- and deadlock/serialization rollback counts:
SELECT datname, deadlocks FROM pg_stat_database WHERE datname = current_database();
メソッド query_stat。ロック待ち/デッドロックの件数があれば、競合の検出結果は established と確認できます。継続的な統計がなければ、directional のままです。
検出結果
schema/finding.schema.json に従って検出結果を出力します。例:
M14.accounts.lost_update_balance— ロック/アトミックな更新を行わない読み取り・変更・書き込み(severity:4、fail、軸performance、confidencedirectional、fixable: proposed— アトミックなUPDATEまたはFOR UPDATE)。M14.jobs.no_skip_locked—SKIP LOCKEDを使用しないワーカーポーリング(severity:3、warn、directional、fixable: proposed)。M14.payments.non_idempotent_write— 冪等性キーのない KV/doc 書き込み(severity:3、warn、directional、fixable: advisory)。
各検出結果では、evidence.observed にトランザクション/更新コードをそのまま引用します(シークレットは伏せます)。verification.reproduce には、上記の分離レベル/統計クエリを $DATABASE_URL を参照する形で記載します。expected_impact は、信頼度タグ付きの範囲で示します(単独の%値は使用しません)。
誠実さ
- 異常が 発生し得る ことは、競合状態が 実際に発生している 証明にはなりません。実際のデッドロック/シリアライゼーション統計がなければ、これらは
directionalとし、上限設定には決して使いません。 - ほとんどのワークロードでは
READ COMMITTEDが適切です。一律に欠陥とみなすのではなく、より強い分離レベルを必要とする不変条件に対してのみ指摘してください。 - 同時実行性の修正はアプリケーションロジックの変更(ロック、アトミック操作、冪等性キー)にあたるため、
proposed/advisoryとし、autoにはしません。