Repository navigation
Description
Summary
Review the error-origin contract of asynchronous reads. Their broad guard encloses driver I/O, document deserialization, and IAsyncObserver.OnNextAsync. It translates by exception type, so application code throwing a MongoException is reported as a storage failure even when the adapter's database operation succeeded.
Priority: P3 (contract/compatibility investigation). The wrapping behavior is established by source inspection; whether to change it requires an explicit contract decision. No production incident or new executable reproduction is claimed.
Evidence
At develop 8f580f1a, also present at #106 head e5ea11b6:
- An async stream read runs cursor acquisition, each batch,
doc.ToCommit(_serializer), andobserver.OnNextAsyncinsideObserveMongoAsync. - The wrapper maps any
MongoConnectionExceptionin that operation toStorageUnavailableExceptionand any otherMongoExceptiontoStorageException, then delivers it toOnErrorAsync. - In contrast, Fix deferred MongoDB read exception translation #106's synchronous iterator guards acquisition/batch advancement while leaving projection outside the guard, explicitly preserving serializer exception identity.
- Terminal observer callbacks are already outside the guarded operation after [P2] Task cancellation is swallowed and CommitAsync completes successfully with null #86/fix: propagate async cancellation and await observer callbacks (#86) #99. A failure from
OnCompletedAsyncorOnErrorAsyncis not theOnNextAsyncissue described here.
This can obscure the failing component and influence logging/retry decisions. For example, an observer may use MongoDB independently of this adapter. A MongoDB exception from that work does not establish a failure of the event-store read.
Deterministic characterization tests
With a successful fake collection/cursor supplying a valid commit, exercise public async reads using:
- An application serializer that throws a sentinel
MongoException,MongoConnectionException, or ordinary exception. - A successful serializer and observer whose
OnNextAsyncthrows each sentinel, both synchronously and through a faulted task. - The same exception types injected from driver acquisition and later batches.
Record error identity, wrapper/inner exception, which observer method receives it, task result, notification counts, and cursor disposal. Current source predicts storage wrapping for MongoDB-typed serializer/OnNextAsync failures, but identity preservation for ordinary exceptions. Keep terminal-callback failures separate.
Decision and acceptance criteria
- Decide whether storage translation should reflect the operation's origin or exception type, and document serializer and observer behavior explicitly.
- If narrowing the translation boundary, retain actual driver-error translation, streaming/order/backpressure, early stop, cancellation behavior, exactly-once terminal notification, and cursor disposal.
- Test all observer read families, including deleted commits and stream heads; audit task-returning snapshot/commit paths separately before expanding scope.
- Preserve protected virtual
TryMongoAsyncextension behavior or document any change. Changing wrapper types or error routing is observable even without signature changes. - Retain the cancellation and callback guarantees covered by #86 tests. Do not accidentally reintroduce swallowed cancellation or duplicate terminal notifications.
- If existing behavior is intentional, document and test it rather than imposing sync/async identity parity without review.
Distinct from #91/#106's deferred driver failures, #86/#99's cancellation/terminal-callback fixes, and #42's serializer representation/schema contract. No code change is requested by this filing.
Co-authored by Alessandro Giorgetti and Codex (OpenAI).
Activity