Skip to content

[P3] Define disposal behavior during active asynchronous reads #109

Open

Description

@AGiorgetti

Summary

Decide and document what disposing MongoPersistenceEngine means for an asynchronous read that has already started. The current async guard checks IsDisposed once before the operation; it does not recheck between cursor batches. #106 introduces per-batch checks for synchronous enumeration.

Priority: P3 (lifetime-contract/design question). This is not an established correctness defect, cancellation bug, or resource leak. Disposal may intentionally prevent new operations while allowing active reads to finish; the intended contract needs to be explicit before changing behavior.

Evidence and scope

At develop 8f580f1a:

These differences are confirmed by source inspection. No active-read disposal scenario was executed as part of this filing.

Deterministic characterization scenario

  1. Return a fake async cursor with at least two batches and observable acquisition, advancement, callback, and disposal counts.
  2. Pause the observer after receiving the first batch, call engine.Dispose(), then release the observer so the read can request the next batch.
  3. Record whether another MoveNextAsync occurs, which records are delivered, the terminal notification/task result, and whether the cursor is disposed exactly once.
  4. Separately gate acquisition and an in-flight advancement, disposing while each is pending. Avoid timing-based sleeps.
  5. Compare with disposal before invocation and with the synchronous behavior in Fix deferred MongoDB read exception translation #106.

Current async source permits continued advancement after disposal if the driver and observer continue normally. That observation alone does not choose the desired contract.

Decision and acceptance criteria

  • State whether active async reads may finish, fail at a defined boundary, or require callers to cancel/await them before disposing the engine.
  • Specify behavior before invocation, during acquisition, within a batch, between batches, and while an observer callback is pending.
  • Define the observable task result and observer error/completion policy without conflating disposal with request-token cancellation.
  • Document cursor ownership and guarantee cleanup on completion, early observer stop, failure, and cancellation.
  • If interruption is desired, assess compatibility for shared engines and concurrent readers. Per-batch checks alone do not promise atomic exclusion of a disposal race or immediate interruption.
  • Add deterministic tests for the chosen contract across async observer read families, preserving [P2] Task cancellation is swallowed and CommitAsync completes successfully with null #86/fix: propagate async cancellation and await observer callbacks (#86) #99 cancellation and terminal-callback guarantees. Explain intentional sync/async differences if retained.

Keep this separate from #91/#106's driver-exception translation and from the already-disposed cleanup guard gap in #107. Historical #16 concerned background stream-head writes after disposal, not the active async read contract here.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions