fix(sitecallaudit): UpdatedAtUtc index + per-row pull resilience + UTC-convention + first-cycle test (review)
This commit is contained in:
@@ -41,8 +41,10 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// <see cref="StatusCode.DeadlineExceeded"/>, <see cref="StatusCode.Cancelled"/>,
|
||||
/// bare <see cref="HttpRequestException"/> / <c>SocketException</c>) are caught
|
||||
/// and collapsed to an empty response so one offline site never sinks the rest
|
||||
/// of the reconciliation tick. Any other fault (e.g. a malformed reply that
|
||||
/// fails DTO mapping) is also swallowed to empty: reconciliation is best-effort.
|
||||
/// of the reconciliation tick. Any other transport/protocol fault is also
|
||||
/// swallowed to empty: reconciliation is best-effort. Per-row DTO mapping faults
|
||||
/// (e.g. a single unparseable <c>TrackedOperationId</c>) are narrower still —
|
||||
/// the offending row is skipped+logged and the rest of the batch is returned.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Testability.</b> The unary call is reached through the
|
||||
@@ -138,15 +140,30 @@ public sealed class GrpcPullSiteCallsClient : IPullSiteCallsClient
|
||||
return Empty;
|
||||
}
|
||||
|
||||
// Map proto DTOs to central SiteCall entities, re-stamp SourceSite from
|
||||
// the dialed siteId (the site leaves it empty), and order oldest-first by
|
||||
// UpdatedAtUtc (the wire is already ordered by the site read, but the
|
||||
// contract is explicit, so sort defensively).
|
||||
var siteCalls = reply.Operationals
|
||||
.Select(SiteCallDtoMapper.FromDto)
|
||||
.Select(sc => sc with { SourceSite = siteId })
|
||||
.OrderBy(sc => sc.UpdatedAtUtc)
|
||||
.ToList();
|
||||
// Map proto DTOs to central SiteCall entities PER-ROW so one malformed
|
||||
// operational (e.g. an unparseable TrackedOperationId) is skipped+logged
|
||||
// rather than sinking the whole batch through the outer catch-all. Each
|
||||
// survivor is re-stamped with SourceSite from the dialed siteId (the site
|
||||
// leaves it empty).
|
||||
var siteCalls = new List<SiteCall>(reply.Operationals.Count);
|
||||
foreach (var dto in reply.Operationals)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sc = SiteCallDtoMapper.FromDto(dto) with { SourceSite = siteId };
|
||||
siteCalls.Add(sc);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex,
|
||||
"PullSiteCalls dropped a malformed operational row from site {SiteId} (id='{Id}'); continuing with the rest of the batch.",
|
||||
siteId, dto.TrackedOperationId);
|
||||
}
|
||||
}
|
||||
|
||||
// Order oldest-first by UpdatedAtUtc (the wire is already ordered by the
|
||||
// site read, but the contract is explicit, so sort defensively).
|
||||
siteCalls.Sort((a, b) => a.UpdatedAtUtc.CompareTo(b.UpdatedAtUtc));
|
||||
|
||||
return new PullSiteCallsResponse(siteCalls, reply.MoreAvailable);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user