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:
@@ -4,6 +4,17 @@ namespace ScadaLink.Commons.Interfaces.Transport;
|
||||
/// Scoped service the bundle importer sets to thread a BundleImportId through to
|
||||
/// the audit log entries emitted by the audited repository methods invoked during
|
||||
/// ApplyAsync. AuditService reads this and stamps every AuditLogEntry it writes.
|
||||
/// <para>
|
||||
/// Re-entrancy / thread-safety: mutating <see cref="BundleImportId"/> is NOT
|
||||
/// thread-safe. The service is registered scoped, and the assumed usage is a
|
||||
/// single Blazor Server circuit (or single API request) at a time — within that
|
||||
/// scope <see cref="BundleImporter.ApplyAsync"/> is the sole writer, and the
|
||||
/// audit service is the sole reader, in a strictly sequential await chain.
|
||||
/// Callers that perform concurrent imports within a shared scope (e.g. two
|
||||
/// <c>ApplyAsync</c> calls awaited via <c>Task.WhenAll</c> on the same circuit)
|
||||
/// MUST serialize access externally — there is no internal lock and the last
|
||||
/// writer wins, which would cross-contaminate audit rows between imports.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IAuditCorrelationContext
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user