de65ca2b5d
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
51 lines
2.9 KiB
C#
51 lines
2.9 KiB
C#
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>
|
|
{
|
|
// Also what keeps SyncBackgroundService's unchecked backoff doubling overflow-safe.
|
|
private static readonly TimeSpan ReconnectBackoffCeiling = TimeSpan.FromDays(1);
|
|
|
|
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}'.");
|
|
|
|
// ApiKey needs no validation: null/empty legitimately means "no key auth".
|
|
|
|
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}.");
|
|
else if (options.ReconnectBackoffMax > ReconnectBackoffCeiling)
|
|
failures.Add($"LocalDb:Replication:ReconnectBackoffMax must be at most {ReconnectBackoffCeiling}; got {options.ReconnectBackoffMax}.");
|
|
if (options.MaintenanceInterval <= TimeSpan.Zero)
|
|
failures.Add($"LocalDb:Replication:MaintenanceInterval must be greater than zero; got {options.MaintenanceInterval}.");
|
|
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;
|
|
}
|
|
}
|