fix: avoid multikey onCommit use-after-free - #489
Conversation
WalkthroughThe change skips Eloq multikey recovery callbacks and adds a configurable stress test. The test validates multikey metadata, transactions, failed writes, repeated database destruction, concurrent workers, and persistent canary behavior across restarts. ChangesMultikey commit validation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant StressWorkers
participant MongoDBServer
participant MultikeyIndex
participant PersistentCanary
StressWorkers->>MongoDBServer: perform direct and transactional writes
MongoDBServer->>MultikeyIndex: commit multikey metadata
StressWorkers->>MongoDBServer: run explain and checkpoint validation
StressWorkers->>PersistentCanary: create or retain restart canary
PersistentCanary-->>MongoDBServer: validate persistent data
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/jstests/eloq_basic/multikey_catalog_commit_stress.js (1)
252-255: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winStrengthen the plan-cache warmup to actually exercise the multikey/plan-cache interaction.
This warmup runs the
{"a.b": base}shape three times before the collection becomes multikey, andassertHealthyMultikeyQueries(Lines 168-176) later runs the same shape ({"a.b": base + 2}, no.hint()) after the index becomes multikey. This is the right shape to target a stale, pre-multikey plan-cache entry.However, in every scenario only one array element per document matches the queried value, so a stale plan that assumes the index is not multikey would still return the correct
itcount()of 1 - it would not need to deduplicate matches from multiple array positions in the same document. As currently written, the test may pass even if a query plan cached before the commit incorrectly persists past the multikey change, which is exactly the class of bug this PR's callback-skip change could introduce for Eloq if the plan cache is not otherwise invalidated.Add a case where the warmed query value matches more than one element of the same array field in a single document (for example a query value appearing twice in
tags), so a stale non-multikey-aware plan would surface as an incorrect duplicate count.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/jstests/eloq_basic/multikey_catalog_commit_stress.js` around lines 252 - 255, Strengthen the warmup in the multikey plan-cache scenario so its queried value matches multiple elements of the same document array, such as a duplicated value in tags. Update the setup and assertions around the warmup and assertHealthyMultikeyQueries to verify the expected deduplicated document count, ensuring a pre-multikey cached plan would produce a detectable incorrect duplicate count.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/jstests/eloq_basic/multikey_catalog_commit_stress.js`:
- Around line 252-255: Strengthen the warmup in the multikey plan-cache scenario
so its queried value matches multiple elements of the same document array, such
as a duplicated value in tags. Update the setup and assertions around the warmup
and assertHealthyMultikeyQueries to verify the expected deduplicated document
count, ensuring a pre-multikey cached plan would produce a detectable incorrect
duplicate count.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: dfa07f74-1e29-46b4-8a6a-a0b3883cc652
📒 Files selected for processing (2)
src/mongo/db/catalog/index_catalog_entry_impl.cpptests/jstests/eloq_basic/multikey_catalog_commit_stress.js
What changed
IndexCatalogEntryImplmultikey commit callback for the Eloq storage engine.Why
IndexCatalogEntryImpl::setMultikey()persists the new multikey metadata and then registers anonCommitcallback that captures the entry through a rawthispointer.Eloq publishes catalog changes as a new schema version. During commit, refreshing that schema can replace the cached
Collectionand destroy itsIndexCatalogEntryImplbefore RecoveryUnit callbacks run. The callback can then dereference a dangling pointer.For Eloq, the refreshed catalog entry is rebuilt from committed metadata and already contains the committed multikey state and path-level metadata. The callback is therefore unnecessary. Transaction-local multikey tracking remains unchanged for read-own-writes.
Other storage engines retain the original callback that updates
_isMultikey, merges multikey paths, and clears the plan cache.Testing
Added
tests/jstests/eloq_basic/multikey_catalog_commit_stress.js, covering:CannotIndexParallelArraysResults:
multikey_index.jsandread_own_multikey_writes.jspassed.install-corebuild passed.node --checkandgit diff --checkpassed.Summary by CodeRabbit
Bug Fixes
Tests