レースコンディション
複数のプロセスやスレッドが共有リソースに同時アクセスし、実行順序によって結果が変わる不具合
並行処理品質
レースコンディションとは
レースコンディション (Race Condition) は、複数のプロセスやスレッドが共有リソース (変数、ファイル、DB レコード) に同時にアクセスし、実行順序によって結果が予期しない値になる不具合である。再現が困難で、テストで検出しにくい厄介なバグだ。
典型的な例: 在庫の二重引き当て
典型的な例: 在庫の二重引き当てを図で示す。
在庫: 1個
リクエスト A: 在庫を確認 → 1個ある → 購入処理
リクエスト B: 在庫を確認 → 1個ある → 購入処理 ← A の更新前に読んだ!
結果: 在庫 1 個なのに 2 人が購入 → 在庫が -1 に
「確認」と「更新」の間に別のリクエストが割り込むことで発生する。これを TOCTOU (Time of Check to Time of Use) 問題と呼ぶ。
DynamoDB での対策: 条件付き書き込み
DynamoDB での対策: 条件付き書き込みのコード例を示す。
// ❌ レースコンディション: 読み取りと書き込みの間に割り込まれる
const item = await ddb.send(new GetCommand({ TableName: 'Products', Key: { id: 'prod-1' } }));
if (item.Item!.stock > 0) {
await ddb.send(new UpdateCommand({
TableName: 'Products',
Key: { id: 'prod-1' },
UpdateExpression: 'SET stock = stock - 1',
}));
}
// ✅ 条件付き書き込み: アトミックに確認と更新を実行
await ddb.send(new UpdateCommand({
TableName: 'Products',
Key: { id: 'prod-1' },
UpdateExpression: 'SET stock = stock - 1',
ConditionExpression: 'stock > :zero',
ExpressionAttributeValues: { ':zero': 0 },
}));
// stock が 0 以下なら ConditionalCheckFailedException がスロー
RDB での対策
楽観的ロック (バージョン番号)
-- 読み取り時にバージョンを取得
SELECT stock, version FROM products WHERE id = 'prod-1';
-- stock: 5, version: 3
-- 更新時にバージョンを条件に含める
UPDATE products SET stock = stock - 1, version = version + 1
WHERE id = 'prod-1' AND version = 3;
-- 他のトランザクションが先に更新していたら、version が 4 になっており、0 行更新
悲観的ロック (SELECT FOR UPDATE)
BEGIN;
SELECT stock FROM products WHERE id = 'prod-1' FOR UPDATE; -- 行ロック
UPDATE products SET stock = stock - 1 WHERE id = 'prod-1';
COMMIT;
Lambda でのレースコンディション
Lambda は同時に複数のインスタンスが実行されるため、共有リソース (DynamoDB、S3) へのアクセスでレースコンディションが発生しやすい。
対策:
- DynamoDB の条件付き書き込み (ConditionExpression)
- DynamoDB のアトミックカウンター (
SET count = count + 1) - 分散ロック (DynamoDB ベースのロック)
- SQS FIFO キューで直列化
競合状態の対策
競合状態の対策を以下にまとめる。
| 対策 | 説明 | AWS での実装 |
|---|---|---|
| ミューテックス | 排他制御 | DynamoDB 条件付き書き込み |
| 楽観ロック | バージョン番号で検出 | DynamoDB ConditionExpression |
| アトミック操作 | 不可分な操作 | DynamoDB UpdateExpression |
| キュー | 順序を保証 | SQS FIFO |
現場での応用を知るには関連書籍も役立つ。
この記事は役に立ちましたか?
関連用語
並行処理
複数のタスクを論理的に同時に進行させるプログラミング手法で、システムのスループットと応答性を向上させる
楽観的ロック
データの読み取り時にロックせず、更新時にバージョンを検証して競合を検出する排他制御
分散ロック
分散システムで複数のプロセスが同じリソースに同時アクセスすることを防ぐ排他制御
ライトアンプリフィケーション
データベースへの 1 回の論理的な書き込みが、内部的に複数回の物理的な書き込みを引き起こす現象
ミューテックス
複数のスレッドが共有リソースに同時アクセスすることを防ぐ排他制御の同期プリミティブ
データベースインデックス
検索クエリの高速化のためにデータベースが維持する補助的なデータ構造