feat(localdb): gRPC sync adapters, background service, replication DI

Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
This commit is contained in:
Joseph Doherty
2026-07-17 23:01:29 -04:00
parent 183901e8ac
commit a64fc26391
7 changed files with 701 additions and 0 deletions
@@ -0,0 +1,41 @@
using Microsoft.Extensions.Options;
namespace ZB.MOM.WW.LocalDb.Replication;
/// <summary>
/// Validates <see cref="ReplicationOptions"/> at startup, accumulating every failure into one result.
/// <see cref="ReplicationOptions.PeerAddress"/> is only required for the initiator role, which the
/// validator cannot distinguish from a passive node, so its format is checked only when present.
/// </summary>
public sealed class ReplicationOptionsValidator : IValidateOptions<ReplicationOptions>
{
public ValidateOptionsResult Validate(string? name, ReplicationOptions options)
{
var failures = new List<string>();
if (!string.IsNullOrEmpty(options.PeerAddress)
&& (!Uri.TryCreate(options.PeerAddress, UriKind.Absolute, out var uri)
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)))
failures.Add(
$"LocalDb:Replication:PeerAddress must be an absolute http/https URI; got '{options.PeerAddress}'.");
if (options.FlushInterval <= TimeSpan.Zero)
failures.Add($"LocalDb:Replication:FlushInterval must be greater than zero; got {options.FlushInterval}.");
if (options.MaxBatchSize <= 0)
failures.Add($"LocalDb:Replication:MaxBatchSize must be greater than 0; got {options.MaxBatchSize}.");
if (options.MaxOplogRows <= 0)
failures.Add($"LocalDb:Replication:MaxOplogRows must be greater than 0; got {options.MaxOplogRows}.");
if (options.MaxOplogAge <= TimeSpan.Zero)
failures.Add($"LocalDb:Replication:MaxOplogAge must be greater than zero; got {options.MaxOplogAge}.");
if (options.TombstoneRetention <= TimeSpan.Zero)
failures.Add($"LocalDb:Replication:TombstoneRetention must be greater than zero; got {options.TombstoneRetention}.");
if (options.ReconnectBackoffMax <= TimeSpan.Zero)
failures.Add($"LocalDb:Replication:ReconnectBackoffMax must be greater than zero; got {options.ReconnectBackoffMax}.");
if (options.MaxHlcDriftAhead <= TimeSpan.Zero)
failures.Add($"LocalDb:Replication:MaxHlcDriftAhead must be greater than zero; got {options.MaxHlcDriftAhead}.");
return failures.Count > 0
? ValidateOptionsResult.Fail(failures)
: ValidateOptionsResult.Success;
}
}