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

No one assigned

    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