Skip to content

IterateLookup behaves incorrectly after Upsert→Delete→Upsert in v2.0beta3 #1630

@KazuhiroNomura

Description

@KazuhiroNomura

Describe the bug

Summary

In v2.0beta3, calling IterateLookup after a Upsert → Delete → Upsert sequence causes either a freeze or missing records, depending on the device type. The same logic works correctly in v1.1.

Steps to Reproduce

  1. Upsert keys 0–252 (253 records total)
  2. Delete all keys 0–252
  3. Upsert keys 0–252 again
  4. Call IterateLookup in a loop with resetCursor:false; in the Reader callback, return CursorRecordResult.Accept | CursorRecordResult.EndBatch when the batch count reaches 64 (MaxCount)

Expected Behavior (v1.1)

All 253 records (keys 0–252) are returned. The loop exits naturally with TotalCount == 253.

Actual Behavior (v2.0beta3)

Two distinct failure modes are observed depending on the device type.

LocalMemoryDevice: freeze / infinite loop

IterateLookup never completes. The scan log shows the following repeating pattern, with OnStop firing repeatedly and OnStart never called between batches:

OnStop → Accept... → OnStop
Accept...
OnStop → Accept... → OnStop
OnStop → Accept... → OnStop
(repeats indefinitely)

② Other devices (e.g. ManagedLocalStorageDevice): missing records

The loop terminates, but keys 0–66 (67 records) are never returned. Only keys 67–252 (186 records) are retrieved. Assert.Equal(253, TotalCount) fails.

Comparison with v1.1

  | v1.1 | v2.0beta3 -- | -- | -- Interface | IScanIteratorFunctions | IScanIteratorFunctions Allocator | SpanByteAllocator | ObjectAllocator LocalMemoryDevice | Works correctly | Freezes Other devices | Works correctly | Keys 0–66 missing

Root Cause Hypothesis

The bug appears to be in how v2.0beta3's IterateLookup handles cursor resumption across batch boundaries (EndBatch) when deleted (tombstoned) records are physically present in the log. Specifically, the resume address calculation may not correctly account for skipped delete records, causing the scan to either loop back to an already-visited region (LocalMemoryDevice) or skip the initial segment of live records entirely (other devices). The absence of OnStart between batches in the LocalMemoryDevice log strongly suggests the cursor state is not being preserved correctly when re-entering the scan after EndBatch.

A minimal reproduction is provided in the attached test code.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions