Files
scadaproj/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Replicator.AkkaDotNet/AkkaSecretsReplicationOptions.cs
T
Joseph Doherty dd0a846b64 feat(secrets): cluster replication via SQL Server and Akka.NET (G-7, 0.2.0)
Secrets were per-node SQLite, so a secret written on one node was invisible to
the rest of a cluster. G-7's design resolved the "shared SQL store vs Akka
replicator" fork to build only the former; both are built here so the choice is
a deployment decision (availability vs partition tolerance) rather than a
library limitation.

Two new packages — ZB.MOM.WW.Secrets.Replicator.SqlServer (shared store, plus a
local-store-with-hub mode) and .Replicator.AkkaDotNet (peer-to-peer over
distributed pub/sub). Core gains ISecretsStoreMigrator, one shared
SecretLastWriterWins predicate so no two stores can disagree on a tie, the
transport-agnostic reconciler, and ReplicatingSecretStore — which closes a real
gap: nothing had ever called ISecretReplicator.PublishAsync, so the seam was
inert and local writes would not have propagated at all.

Verified 182 pass / 1 skip / 0 warnings, including 15 live tests against a real
SQL Server 2022 (the SQLite suite ported case-for-case, so any behavioural
divergence between the stores fails) and a 9-test in-process 2-node Akka
cluster over real remoting. A post-build review caught six defects, all fixed
and now covered: both replication modes could not resolve from the container
(no test had built one), an unbounded fetch that broke past SQL Server's
2100-parameter cap, a poison row that aborted the rest of its batch forever,
Enum.Parse on peer input that could restart the actor in a loop, null crypto
blobs crossing the trust boundary, and a silently dropped pull-read failure.

Packed at 0.2.0 and vulnerability-scanned clean; not yet published to the feed.

Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
2026-07-18 04:08:23 -04:00

41 lines
1.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet;
/// <summary>
/// Configuration for peer-to-peer secret replication over the Akka cluster.
/// </summary>
public sealed class AkkaSecretsReplicationOptions
{
/// <summary>
/// How often this node broadcasts its manifest for anti-entropy. Defaults to 30 seconds.
/// </summary>
/// <remarks>
/// This is the convergence bound, not the propagation latency: a live write reaches peers in
/// milliseconds, and this interval only governs how quickly a <em>dropped</em> write or a
/// rejoining node is repaired. The traffic is one manifest per node per interval — names,
/// revisions, and timestamps, no ciphertext — so shortening it is cheap for a small cluster and
/// grows as O(nodes × secrets).
/// </remarks>
public TimeSpan AnnounceInterval { get; set; } = TimeSpan.FromSeconds(30);
/// <summary>Name of the replication actor within the actor system. Defaults to <c>zb-secret-replication</c>.</summary>
public string ActorName { get; set; } = "zb-secret-replication";
/// <summary>
/// Throws if the options are unusable.
/// </summary>
/// <exception cref="InvalidOperationException">The announce interval is not positive, or the actor name is blank.</exception>
public void Validate()
{
if (AnnounceInterval <= TimeSpan.Zero)
{
throw new InvalidOperationException(
$"Secret replication AnnounceInterval must be positive (was {AnnounceInterval}).");
}
if (string.IsNullOrWhiteSpace(ActorName))
{
throw new InvalidOperationException("Secret replication ActorName must not be blank.");
}
}
}