using Microsoft.Extensions.Options;
namespace ZB.MOM.WW.LocalDb.Replication;
///
/// Validates at startup, accumulating every failure into one result.
/// is only required for the initiator role, which the
/// validator cannot distinguish from a passive node, so its format is checked only when present.
///
public sealed class ReplicationOptionsValidator : IValidateOptions
{
// 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();
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;
}
}