Skip to content

Fix flaky RecoveryRollback test: GC-movable key memory in async tests - #2137

Merged
Ted Hart (TedHartMS) merged 8 commits into
mainfrom
tedhar/flaky-test-failures
Sep 22, 2026
Merged

Ted Hart (TedHartMS) merged 8 commits into
mainfrom
tedhar/flaky-test-failures

Conversation

@TedHartMS

Copy link
Copy Markdown
Contributor

Problem

RecoveryCheck2Tests.RecoveryRollback fails intermittently — ~38% of full
Tsavorite.test.recovery runs, but never in isolation. Both Snapshot and
FoldOver variants fail, with reads returning NotFound or another key's
value (e.g. output = 7, key = 691).

Root cause

A test bug, not a product bug.

SpanByte.FromPinnedVariable captures a raw pointer via Unsafe.AsPointer and
requires non-movable storage. TestSpanByteKey.FromPinnedSpan then retains only
that void* with arr = null — no GC reference. RecoveryRollback passed
async-method locals, so a GC relocation left the store reading stale memory.

The sibling RecoveryCheck1/2/3/4/5 tests in the same file already use an
array-backed key, with the comment "Local variables in an async function can be
moved, so we must use an array for the key."
RecoveryRollback never got it.

Changes

  1. RecoveryRollback: use an array-backed key, matching its sibling tests.
  2. Harden the other 18 occurrences of this pattern in async test methods
    across 9 files — GC-tracked arrays where the raw pointer isn't needed,
    GC.AllocateArray(pinned: true) where FromPinnedVariable is retained.

Tests only; no product code changes. The one site already using a pinned array
is correct and unchanged.

Verification

Before After
RecoveryRollback with [Repeat(40)] 100% fail, aborts after ~6 reps 5/5 runs, all 40 reps
Full recovery assembly (217 tests) ~38% fail (3/8 runs) 0/10 runs

Also green: VersionSwitch 24, session.context 142, ReadAddress 33,
CompletePending/Misc/Namespace 49. dotnet format --verify-no-changes
clean on Tsavorite.slnx.

RecoveryRollback is an async method, so its locals can be relocated by the
GC. It passed SpanByte.FromPinnedVariable(ref key) into
TestSpanByteKey.FromPinnedSpan, which retains only a raw pointer and no GC
reference, so a collection that moved the local left the store reading stale
memory. Reads then returned NotFound or another key's value.

Use an array-backed key, matching the pattern already used by the sibling
RecoveryCheck tests in this file.
SpanByte.FromPinnedVariable captures a raw pointer and requires non-movable
storage, but these async tests passed locals, which the GC may relocate.
TestSpanByteKey.FromPinnedSpan keeps only that pointer and no GC reference,
so a collection could leave an operation reading stale memory.

Back the affected keys and values with stable storage: GC-tracked arrays
where the raw pointer is no longer needed, and GC.AllocateArray(pinned: true)
where FromPinnedVariable is retained. The one site already using a pinned
array is unchanged.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes consistently eliminate unsafe pointers to movable async state without introducing unresolved correctness issues.

Pull request overview

Fixes flaky Tsavorite async tests by preventing raw pointers from referencing GC-movable memory.

Changes:

  • Uses array-backed keys where pointer retention is unnecessary.
  • Uses pinned arrays where SpanByte.FromPinnedVariable remains necessary.
  • Aligns RecoveryRollback with existing recovery-test patterns.
File summaries
File Description
ReadAddressTests.cs Pins values used through raw pointers.
UnsafeContextTests.cs Pins values used by unsafe upserts.
TransactionalUnsafeContextTests.cs Pins transactional keys and values.
SimpleRecoveryTest.cs Uses array-backed value spans.
RecoveryCheckTests.cs Makes rollback keys GC-safe.
CheckpointManagerTests.cs Uses array-backed keys and values.
StateMachineDriverTests.cs Adds reusable array-backed key construction.
NamespaceTests.cs Pins the upsert value.
MiscTests.cs Pins values used during recovery testing.
CompletePendingTests.cs Pins values before pending operations.
Review details
  • Files reviewed: 10/10 changed files
  • Comments generated: 0
  • Review effort level: Balanced (auto)

Note

Copilot is running an experiment and ran this review at Balanced.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@TedHartMS
Ted Hart (TedHartMS) merged commit 438ba50 into main Sep 22, 2026
227 checks passed
@TedHartMS
Ted Hart (TedHartMS) deleted the tedhar/flaky-test-failures branch September 22, 2026 06:10
x@01 (x-at-01) added a commit to webc-fork/garnet that referenced this pull request Oct 4, 2026
…soft#2156), path composition normalize (microsoft#2147), refuse serve w/o HLog checkpoint (microsoft#2153), retire outdated checkpoints (microsoft#2144), checkpoint serialize race (microsoft#2136), RecoveryRollback flaky (microsoft#2137)
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.

3 participants