fix(transport): robust failure-audit when rollback throws + doc clarifications
Address one Blocker and three Important findings from code review of
2c34f12 (BundleImporter.ApplyAsync):
- BLOCKER: wrap RollbackAsync in nested try/catch so a rollback fault
does not swallow the BundleImportFailed audit row. Dispose the
failed transaction before the audit-write so the new SaveChangesAsync
uses a fresh implicit transaction instead of enlisting in the broken
one. Surface the rollback exception's message on the failure row
alongside the original cause, and swallow audit-write faults per the
design's best-effort-audit invariant. Add regression integration
test using a SQLite transaction interceptor that throws on rollback.
- Document re-entrancy assumption on IAuditCorrelationContext: scoped
lifetime, single circuit, concurrent imports within a shared scope
must serialize externally.
- Document repository audit responsibility on BundleImporter: repos
are thin EF wrappers; ApplyAsync writes audit rows explicitly. If
repos ever start emitting audit rows, the explicit calls here must
be removed to avoid double-logging.
- Document BundleSessionStore thread-safety: ConcurrentDictionary
primitives are safe under concurrent callers; BundleSession itself
is not thread-safe.
This commit is contained in:
@@ -11,6 +11,17 @@ namespace ScadaLink.Transport.Import;
|
||||
/// at read time (<see cref="Get"/>) and on-demand via <see cref="EvictExpired"/>;
|
||||
/// there is no background timer.
|
||||
/// <para>
|
||||
/// Thread-safety: backed by <see cref="ConcurrentDictionary{TKey,TValue}"/> of
|
||||
/// <see cref="Guid"/> to <see cref="BundleSession"/>. All store operations
|
||||
/// (<see cref="Get"/> / <see cref="Open"/> / <see cref="Remove"/> /
|
||||
/// <see cref="EvictExpired"/>) use the concurrent dictionary's safe primitives
|
||||
/// (<c>TryGetValue</c>, indexer assignment, <c>TryRemove</c>) and are safe
|
||||
/// under concurrent callers. The <see cref="BundleSession"/> instance itself
|
||||
/// is NOT thread-safe — callers that share a session reference (e.g. two
|
||||
/// importers mutating <c>FailedUnlockAttempts</c> on the same session) MUST
|
||||
/// serialize their mutations on that shared reference.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// TTL is supplied by the importer via <see cref="BundleSession.ExpiresAt"/>;
|
||||
/// this store does not impose its own. The injected <see cref="TimeProvider"/>
|
||||
/// is used purely to determine <c>now</c> when checking <c>ExpiresAt</c>, which
|
||||
|
||||
Reference in New Issue
Block a user