Skip to content

Fix recycle-bin identity collisions and metadata cleanup ordering (#87) - #97

Merged
AGiorgetti merged 5 commits into
developfrom
feature/fix-recycle-collisions-87
Oct 29, 2026
Merged

AGiorgetti merged 5 commits into
developfrom
feature/fix-recycle-collisions-87

Conversation

@AGiorgetti

@AGiorgetti AGiorgetti commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Scope

Addresses #87 using the selected original option 1. Broader lifecycle/recycle-bin redesign is tracked in #96.

  • Assign one random internal stored StreamId per deletion attempt and retain OriginalBucketId/OriginalStreamId.
  • Recycle commits before deleting stream heads and snapshots, in synchronous and asynchronous paths.
  • Preserve public original-stream reads, legacy fallback, checkpoint values and existing cleanup/index definitions.
  • Document rollout, recovery and concurrency limits in docs/Recycle-Bin.md. Additive original-stream lookup index only; no bucket encoding, dependency upgrade or [P2] Exact-ID explain assertion rejects MongoDB 7 IDHACK despite using the _id index #94 change.

Tests first

The first commit, 28ebbd5, contains regression tests only, against unchanged develop 286c0ae:

  • MongoDB 7.0.45 / net10.0: 12 expected failures, 10 passes, 0 skips.
  • Reproduces cross-bucket and delete/recreate collisions on GetFrom_Index, plus metadata removal before a rejected recycle update.

The following fix adds coverage for mixed legacy/new reads, live-document isolation, retained checkpoints, cleanup, and retry from a deliberately seeded partial-update state.

Indexing follow-up

Tests-first commit 4f5ca52 captures actual sync/async queries over 18,280 records (12,240 recycled, including legacy/new formats, multiple source buckets and deletion attempts). Without the new index, both regression tests fail: every recycle lookup examines 12,240 keys/documents and performs blocking SORT, despite correct results.

The fix adds nonunique GetFrom_RecycleBin_Index on {BucketId, OriginalStreamId, StreamRevisionFrom, StreamRevisionTo}, partial on BucketId: ':rb'. Existing unique indexes are unchanged. MongoDB 7.0.45 explain results:

  • Full mixed history: 160 results, 160 keys / 200 document examinations; SORT_MERGE, no blocking SORT.
  • Overlap window [32,49]: 40 results, 72 keys / 50 document examinations.
  • New-only, legacy-only, absent-stream and live-stream queries also pass, with sync/async filter/sort parity.
  • Populated-store initialization adds the index without changing documents/old indexes; repeated initialization is idempotent.

Changelog and rollout docs cover index-build/write/storage costs and permissions. No latency/throughput benchmark or lifecycle redesign is claimed.

Final local validation

Exact published head: 8c47929; tree: 9fc05977401ff94a917e3c19a9d6f82473e1538a.

  • Whole solution Release build, including net472: 0 errors; existing warnings remain.
  • On each of net8.0, net9.0 and net10.0: 536 core tests passed; 278 adapter tests passed and 1 known baseline test failed; 0 skipped. All five new query-plan/index-initialization tests passed.
  • The sole adapter failure is the unchanged [P2] Exact-ID explain assertion rejects MongoDB 7 IDHACK despite using the _id index #94 IDHACK query-plan portability assertion, Stream_head_delete_by_id_should_use_the_builtin_id_index. It has not been suppressed or weakened.
  • Focused command: dotnet test src/NEventStore.Persistence.MongoDB.Tests/NEventStore.Persistence.MongoDB.Core.Tests.csproj -c Release -f net10.0 --filter FullyQualifiedName~Issue_87

MongoDB 7.0.45 and SDK 10.0.403 were used on dot's cloud computer. net472 was compiled, not executed on Linux. MongoDB 8 coverage is provided by the repository CI service container. Independent source review found no blocker within the bounded scope.

Exact-head GitHub CI

PR CI run 40312108730 and push CI run 40312099115 both completed successfully on head 8c47929. Each passed Ubuntu and Windows builds and the MongoDB 8 Linux test matrix for net8.0, net9.0 and net10.0. No rerun, test suppression or workflow change was needed.

Compatibility and limits

New code reads existing recycled documents without backfill. Their lost original bucket remains unknown. Public BucketId remains :rb; OriginalBucketId is stored provenance only. Original-stream recycle queries combine matching histories across buckets and deletion attempts.

Old binaries/raw queries expose the synthetic stored ID for new records, so upgrade recycle readers before new-format deletion. UpdateMany is not atomic across documents; retries give remaining live commits another token. No durable lifetime grouping, automatic restart recovery, concurrent-delete/recreate fencing, or atomic lifecycle guarantee is claimed. Tests seed partial persisted state rather than killing a process.

Controlled deletion/recovery must exclude concurrent stream writes/recreation and drain background metadata work; readers needing all-or-nothing visibility also need that boundary. Default metadata writes are unacknowledged, so successful return is not a verified cleanup barrier. Existing asynchronous cancellation defects remain separately scoped to #86.

Draft for review. Do not merge or publish packages as part of this task.

Co-authored by Alessandro Giorgetti and Codex (OpenAI).

Baseline MongoDB 7.0.45/net10 run: 12 expected failures, 10 passes, 0 skips.
Product source is unchanged in this commit.

Co-authored by Alessandro Giorgetti and Codex (OpenAI).
Use per-attempt stored identities with original bucket and stream metadata,
recycle before metadata cleanup, and preserve legacy/public read semantics.
Document recovery limits and follow-up #96.

Co-authored by Alessandro Giorgetti and Codex (OpenAI).
Capture actual sync/async find filters and sorts over 18,280 mixed commits. Baseline MongoDB 7.0.45/net10.0: 2 expected failures, 0 skips. Recycle lookups examine all 12,240 recycled keys/documents and use blocking SORT; result semantics remain correct.

Co-authored by Alessandro Giorgetti and Codex (OpenAI).
Add a nonunique recycle-only compound index without changing existing unique indexes. Verify full sync/async query plans, legacy/new selection, populated-store upgrade, and repeated initialization. Document additive migration, build/write/storage costs, and retained reader compatibility limits.

MongoDB 7.0.45: 160 keys and 200 document examinations for 160 mixed-history results instead of 12,240 keys/documents and blocking SORT. Release build including net472 succeeds. Each net8/net9/net10 run: 536 core passes; 278 adapter passes and the unchanged #94 IDHACK assertion failure; zero skips.

Co-authored by Alessandro Giorgetti and Codex (OpenAI).
@AGiorgetti

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T09:30:25.329451Z 8c47929 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 8c4792930c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@AGiorgetti
AGiorgetti merged commit 8cdc31d into develop Oct 29, 2026
10 checks passed
@AGiorgetti
AGiorgetti deleted the feature/fix-recycle-collisions-87 branch October 29, 2026 09:33
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.

1 participant