-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Checkpoints
-
None
-
Storage Engines - Transactions
-
53.111
-
SE Transactions - 2026-09-11
-
3
-
v9.0, v8.3, v8.0, v7.0
Background
Investigating WT-18590 (checkpoint prepare stalling on sweep's dhandle rwlock) turned up a second, independent cost in the same code path: _wt_session_lock_checkpoint unconditionally calls _wt_evict_file_exclusive_on before flushing a checkpoint handle from the cache, for every handle a checkpoint gathers.
That handle is acquired WT_DHANDLE_EXCLUSIVE | WT_DHANDLE_LOCK_ONLY a few lines above, which never opens the underlying tree. With no open tree there is no root page, so the __wt_evict_file flush that follows returns immediately — the evict_disabled/eviction-off bracket around it protects a call that had nothing to do.
_wt_evict_file_exclusive_on is not cheap to call speculatively: it takes the connection-wide eviction walk lock, bumps pass_intr to interrupt the eviction server's pass, takes the eviction pass and queue locks, clears the tree from every eviction queue, and spins for btree->evict_busy to drain. Paying that once per handle gathered, for a no-op flush, both slows the checkpoint down directly and holds the same lock other callers need — including the sweep server, which takes it once per handle it closes (sweep_close_dhandle_locked / _wt_conn_dhandle_close).
Fix (implemented)
Only call _wt_evict_file_exclusive_on / wt_evict_file_exclusive_off when the handle is actually open (F_ISSET(session->dhandle, WT_DHANDLE_OPEN)). _wt_evict_file already asserts btree->evict_disabled > 0 || !F_ISSET(dhandle, WT_DHANDLE_OPEN), so an unopened handle needs no protection — the assertion is exactly the condition being restored.
Reproducer results
15,000 tables, a fan-out thread reopening every table, a rotating dirty set, and a checkpoint every 0.5s; 450s runs from identical fresh databases, unsampled. Isolating this change against the WT-18590 base (before the sweep-direction fix) roughly doubled sweep's close throughput per checkpoint (measured 103→338 and 184→276 handles closed per checkpoint across paired runs) and dropped the resident handle count by ~1,500, consistent with the gather no longer starving sweep of the same lock.
Related
WT-18590— the sweep/gather walk-direction fix this was found alongside; that ticket covers the acquire-path stall, this one covers the per-handle eviction-gate cost. The two are independent and were split into separate PRs.