Skip to content

[DO NOT MERGE] fix: return documents in insertion order for queries without ORDER BY#73

Draft
McNultyyy wants to merge 2 commits into
mainfrom
mcnultyyy/fix-query-insertion-order
Draft

[DO NOT MERGE] fix: return documents in insertion order for queries without ORDER BY#73
McNultyyy wants to merge 2 commits into
mainfrom
mcnultyyy/fix-query-insertion-order

Conversation

@McNultyyy
Copy link
Copy Markdown
Collaborator

Summary

Fixes #72 — Queries without ORDER BY now return documents in insertion order, matching real Cosmos DB behavior.

Problem

InMemoryContainer._items is a ConcurrentDictionary which does not guarantee enumeration order. GetAllItemsForPartition() iterated this dictionary directly, causing documents to be returned in hash-bucket order rather than insertion order.

Solution

Added an _insertionOrder list (List<(string Id, string PartitionKey)>) protected by a lock that tracks the order in which documents are inserted. Key behavioral rules:

  • Create: appends key to end of list
  • Replace: does NOT change position (preserves original insertion position)
  • Upsert existing: does NOT change position
  • Upsert new: appends to end
  • Delete: removes from list
  • Delete + recreate: new position at end

GetAllItemsForPartition() now snapshots this ordered list and iterates it instead of the dictionary, ensuring queries without ORDER BY return documents in insertion order.

All mutation sites are updated: typed and stream variants of Create/Upsert/Delete/Replace, bulk delete by partition key, TTL eviction, state import/export, snapshot restore, and point-in-time restore.

Behavioral Source

Observed behavior on the Windows Cosmos DB Emulator (priority 6): documents returned by queries without ORDER BY are in insertion order.

Testing

  • 6 new integration tests covering: single partition order, cross-partition order, replace preserves order, delete+recreate moves to end, upsert new at end, upsert existing preserves position
  • All 298 integration tests pass ✅
  • All unit tests pass ✅

Version

Bumped 4.0.17 → 4.0.18

Queries without ORDER BY now return documents in insertion order,
matching real Cosmos DB behavior. Previously documents were returned
in hash-map order due to ConcurrentDictionary enumeration.

Added insertion-order tracking list to InMemoryContainer that maintains
document position across create, replace, upsert, and delete operations.
GetAllItemsForPartition() now iterates this ordered list instead of the
dictionary directly.

Fixes #72

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@McNultyyy McNultyyy requested a review from lemonlion as a code owner May 20, 2026 13:48
@McNultyyy McNultyyy marked this pull request as draft May 20, 2026 13:48
Comprehensive edge case tests covering:
- Large batch (55 docs) insertion order
- Multi-partition interleaved insertion order
- Repeated replaces preserving position
- Mixed operations sequence (create/delete/replace/upsert)
- SELECT projections maintaining order
- WHERE filter preserving relative order
- TOP and OFFSET/LIMIT pagination order
- Empty container and single document edge cases
- Delete all + recreate ordering
- Upsert-only creates ordering
- DISTINCT VALUE preserving first occurrence order
- COUNT and SUM aggregates not crashing
- Cross-partition vs single-partition order
- Rapid sequential creates order
- ORDER BY overriding insertion order
- Range filter preserving relative order
- Multiple non-consecutive deletes preserving remaining order

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@McNultyyy McNultyyy changed the title fix: return documents in insertion order for queries without ORDER BY [DO NOT MERGE] fix: return documents in insertion order for queries without ORDER BY May 20, 2026
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.

Queries without ORDER BY do not return documents in insertion order

1 participant