From de65ca2b5de602bfd2345133cbe41c4840a57252 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 18 Jul 2026 01:20:08 -0400 Subject: [PATCH] =?UTF-8?q?polish(localdb):=20final=20integration-review?= =?UTF-8?q?=20fixes=20=E2=80=94=20drift=20XML=20doc,=20trim=20core=20deps,?= =?UTF-8?q?=20metrics=20doc/encapsulation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts --- .../ZB.MOM.WW.LocalDb.Replication/LocalDbMetrics.cs | 12 ++++++++++-- .../LocalDbSchemaMismatchException.cs | 2 ++ .../LocalDbSyncService.cs | 7 +++++-- .../ReplicationOptions.cs | 6 +++++- .../ReplicationOptionsValidator.cs | 2 ++ .../SyncBackgroundService.cs | 7 +++++-- .../Registration/LocalDbRegistrationException.cs | 2 ++ .../src/ZB.MOM.WW.LocalDb/ZB.MOM.WW.LocalDb.csproj | 2 -- 8 files changed, 31 insertions(+), 9 deletions(-) diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbMetrics.cs b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbMetrics.cs index 50ed31d..143dbde 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbMetrics.cs +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbMetrics.cs @@ -24,6 +24,7 @@ public sealed class LocalDbMetrics : IDisposable private readonly Func _utcNow; private bool _disposed; + /// Creates the meter and its instruments. is a test seam for the lag gauge's clock; null uses . public LocalDbMetrics(Func? utcNow = null) { _utcNow = utcNow ?? (() => DateTimeOffset.UtcNow); @@ -41,34 +42,40 @@ public sealed class LocalDbMetrics : IDisposable /// Supplies the current unacked oplog backlog for the localdb.oplog.depth gauge. Invoked /// only at collection time; a cheap synchronous SELECT is acceptable at that frequency. /// - public Func? OplogDepthProvider { get; set; } + public Func? OplogDepthProvider { get; internal set; } /// Supplies the last successful sync time for the localdb.sync.lag.seconds gauge; null means no sync yet, and the gauge then reports no measurement. - public Func? LastSyncUtcProvider { get; set; } + public Func? LastSyncUtcProvider { get; internal set; } /// The backing meter — exposed so a test can filter by instance reference rather than the process-shared meter name. internal Meter Meter => _meter; + /// Adds rows applied from inbound delta batches to localdb.sync.applied (no-op when not positive). public void RecordApplied(long count) { if (count > 0) _applied.Add(count); } + /// Adds inbound rows that lost LWW to localdb.sync.conflicts_discarded (no-op when not positive). public void RecordConflictsDiscarded(long count) { if (count > 0) _conflictsDiscarded.Add(count); } + /// Adds poison / over-drift rows routed to the dead-letter table to localdb.sync.dead_lettered (no-op when not positive). public void RecordDeadLettered(long count) { if (count > 0) _deadLettered.Add(count); } + /// Increments localdb.sync.reconnects for one initiator reconnect attempt. public void RecordReconnect() => _reconnects.Add(1); + /// Increments localdb.sync.snapshots with direction=sent for one completed outbound snapshot. public void RecordSnapshotSent() => _snapshots.Add(1, new KeyValuePair("direction", "sent")); + /// Increments localdb.sync.snapshots with direction=received for one completed inbound snapshot. public void RecordSnapshotReceived() => _snapshots.Add(1, new KeyValuePair("direction", "received")); @@ -99,6 +106,7 @@ public sealed class LocalDbMetrics : IDisposable return [new Measurement(lag)]; } + /// Disposes the backing meter; idempotent. public void Dispose() { if (_disposed) diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSchemaMismatchException.cs b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSchemaMismatchException.cs index 8d45664..b99e4de 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSchemaMismatchException.cs +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSchemaMismatchException.cs @@ -8,8 +8,10 @@ namespace ZB.MOM.WW.LocalDb.Replication; /// public sealed class LocalDbSchemaMismatchException : Exception { + /// Creates the exception with a message describing the schema disagreement. public LocalDbSchemaMismatchException(string message) : base(message) { } + /// Creates the exception with a message and the underlying cause. public LocalDbSchemaMismatchException(string message, Exception innerException) : base(message, innerException) { } } diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSyncService.cs b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSyncService.cs index 3155642..2ef5898 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSyncService.cs +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/LocalDbSyncService.cs @@ -24,8 +24,11 @@ public sealed class LocalDbSyncService : LocalDbSync.LocalDbSyncBase // 0 = idle, 1 = a stream is active. Interlocked-guarded across concurrent calls (singleton service). private int _active; - // status is taken as the public ISyncStatus (SyncStatus is internal and cannot appear in a - // public signature); DI always supplies the concrete SyncStatus this cast recovers. + /// + /// Creates the passive sync service. is taken as the public + /// (SyncStatus is internal and cannot appear in a public signature); + /// DI always supplies the concrete SyncStatus the ctor recovers by cast. + /// public LocalDbSyncService( ILocalDb db, IOptions options, ILoggerFactory loggerFactory, LocalDbMetrics? metrics = null, ISyncStatus? status = null) diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptions.cs b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptions.cs index ed1912f..515cd69 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptions.cs +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptions.cs @@ -35,6 +35,10 @@ public sealed class ReplicationOptions /// Maximum tolerated HLC drift where a peer's clock runs ahead of ours. public TimeSpan MaxHlcDriftAhead { get; set; } = TimeSpan.FromMinutes(5); - /// When true, drift beyond rejects the batch instead of clamping. + /// + /// When true, entries whose HLC physical time exceeds are + /// dead-lettered individually (the rest of the batch still applies); when false, drifted + /// entries are applied and a single warning is logged per session. + /// public bool FailClosedOnDrift { get; set; } } diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptionsValidator.cs b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptionsValidator.cs index 0851dfb..b534265 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptionsValidator.cs +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb.Replication/ReplicationOptionsValidator.cs @@ -22,6 +22,8 @@ public sealed class ReplicationOptionsValidator : IValidateOptionsNumber of connection attempts made this lifetime (one per reconnect loop iteration). internal int ConnectionAttempts; - // status is taken as the public ISyncStatus (SyncStatus is internal and cannot appear in a - // public signature); DI always supplies the concrete SyncStatus this cast recovers. + /// + /// Creates the initiating sync host. is taken as the public + /// (SyncStatus is internal and cannot appear in a public signature); + /// DI always supplies the concrete SyncStatus the ctor recovers by cast. + /// public SyncBackgroundService( ILocalDb db, IOptions options, ILoggerFactory loggerFactory, LocalDbMetrics? metrics = null, ISyncStatus? status = null) diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/Registration/LocalDbRegistrationException.cs b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/Registration/LocalDbRegistrationException.cs index fd7d512..4bd2fce 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/Registration/LocalDbRegistrationException.cs +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/Registration/LocalDbRegistrationException.cs @@ -6,8 +6,10 @@ namespace ZB.MOM.WW.LocalDb.Registration; /// public sealed class LocalDbRegistrationException : Exception { + /// Creates the exception with a message describing why registration was rejected. public LocalDbRegistrationException(string message) : base(message) { } + /// Creates the exception with a message and the underlying cause. public LocalDbRegistrationException(string message, Exception innerException) : base(message, innerException) { } } diff --git a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/ZB.MOM.WW.LocalDb.csproj b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/ZB.MOM.WW.LocalDb.csproj index d9eca86..1fd4137 100644 --- a/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/ZB.MOM.WW.LocalDb.csproj +++ b/ZB.MOM.WW.LocalDb/src/ZB.MOM.WW.LocalDb/ZB.MOM.WW.LocalDb.csproj @@ -21,8 +21,6 @@ - -