Skip to content

Fix Silo TPC-C progress and tuple lifetime under contention - #124

Merged
thawk105 merged 1 commit into
masterfrom
silo-tpcc-contention-fix
Oct 5, 2026
Merged

thawk105 merged 1 commit into
masterfrom
silo-tpcc-contention-fix

Conversation

@thawk105

@thawk105 thawk105 commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Siloで標準TPC-Cミックスを8 worker・1 warehouseで実行すると、測定終了後もworkerがread_internal()で待機し続け、メインスレッドがjoin()から戻れなくなることがあります。本PRでは、ロックが残り続ける経路とTupleの寿命管理を修正し、read/scanが未コミットINSERTに遭遇した場合はトランザクションをabortして通常の再試行へ進めるようにします。

原因と修正内容

Deliveryのwrite setでは、NewOrderのDELETEがOrder/OrderLineのUPDATEより先に処理されます。修正前のwritePhase()は、DELETEで設定したmaxtid.absent=trueを後続のUPDATEにも引き継ぎ、更新したレコードを誤って「存在しない」状態にしていました。その後、そのレコードのロックを取得した更新トランザクションがabortする際、現在のwrite set要素を解除対象に含めず、ロックを残したまま戻っていました。

操作ごとにabsentビットを設定し、absentレコードを検出した場合は現在取得したロックまで解除します。DELETEもabsentレコードを拒否し、同じTupleを二重にGCへ登録することを防ぎます。

関連するトランザクション処理も、次のように修正します。

  • INSERTをMasstreeへ公開した直後、scanで記録したノードの版数を検査する前にwrite setへ登録します。競合エラーで戻った場合も、abortがINSERTを除去できるようにします。
  • abortしたINSERTはindexから除去し、ロック解除済みの削除状態を公開してepoch GCへ渡します。別workerが保持している可能性のあるTupleを即時deleteしません。INSERTをDELETEで取り消す場合も、Tupleの所有権と回収処理を維持します。
  • begin()で保護対象のepochを公開し、validation中もそのepochを保持します。回収用のepochはcommit TIDと分けて管理し、indexからの除去後に取得します。ReclamationEpochへのアクセスもatomicにします。
  • ロックされたabsent INSERTをpoint read/scanで検出したらabortします。レコードのロック待ち中はquitを確認し、待機する設定のwrite-lock経路では観測したTIDを再読込します。
  • read setよりも登録済みのwrite setを優先し、INSERT/UPDATE/DELETEに応じたread-own-writesを返します。同じキーへの繰り返しUPDATEでも更新内容を置き換え、同一New-Order内のStock再更新で数量とカウンタが累積されるようにします。
  • read setの拡張が完了してからscan結果のポインタを構築し、キー順を維持します。vectorの再確保によるポインタの無効化を防ぎます。
  • Order-Statusのsecondary index検索で結果件数が想定外だった場合、プロセスを終了せず、通常のトランザクション失敗として返します。

CCBENCH_BUILD_SILO_TESTSで有効化できる、実行タイミングに依存しない回帰テスト14件を追加し、CIでも実行します。

検証結果

  • 修正前:標準ミックス、8 worker・1 warehouse、extime=3で、3回とも25秒の制限時間を超えました。gdbでは、quitが設定された後もworkerがread_internal()で待機し、メインスレッドがjoin()を待つ状態を確認しました。
  • 回帰テスト14件は、修正前ではすべて失敗またはtimeoutとなり、修正後はRelWithDebInfoとDebug+ASanの両方ですべて成功しました。
  • GCC 11.4.0、-Wall -Wextra -Werrorで全プロトコルのターゲットがビルドできました。
  • 標準ミックス:1 warehouseで1/2/8/16 worker、8 warehouseで8 workerの各条件を3秒ずつ3回実行し、すべて完走しました。最終コードでは、8 worker・1 warehouseの3秒実行をさらに3回と、30秒実行を行い、いずれも完走しました。
  • Debug+ASanの標準ミックス:8 worker・1 warehouseを3回、16 worker・1 warehouseと8 worker・8 warehouseを各1回実行し、すべてASanエラーなく完走しました。ベンチマークはプロセス終了までDBを保持するため、リーク検出は無効にしています。
  • Payment、New-Order、Delivery、Stock-Levelをそれぞれ100%にした実行と、競合を伴うYCSBの動作確認も完走しました。
  • 変更したC++ファイルのclang-format-14検査と、git diff --checkが通りました。

回帰テストの実行方法:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCCBENCH_BUILD_SILO_TESTS=ON
cmake --build build --target silo_regression_test
ctest --test-dir build -R '^silo_regression_test$' --output-on-failure

このcheckoutにはTAMの実装がないため、TAMを有効にした実行は未検証です。上記の実走は、記載した条件と時間における正しさの確認です。性能比較を目的とした測定ではありません。長時間のストレステスト、ThreadSanitizer、GCC 13によるCI相当のビルドは、ローカルでは未検証です。

本修正とPR本文は、AIの支援を受けて作成しました。

Reset absent per operation and release the current lock on an absent target
to prevent Delivery from leaving permanently locked Order records. Register
published INSERT ownership before node validation and retire removed tuples
with epochs sampled after index removal.

Pin epochs at begin, abort reads of pending INSERTs, and observe quit during
lock waits. Preserve staged updates and stable scan results so repeated
Stock updates and read-own-writes behave consistently.

Add 14 deterministic regression cases and run them in CI. All cases fail
against the baseline and pass in normal and ASan builds; the standard mix
with eight workers and one warehouse now completes the 30-second check.
@thawk105
thawk105 merged commit d599519 into master Oct 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant