LocalDb first-enable on a diverged site pair: the active node reconciles stale data before the first sync, so instances never start #135
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during the ScadaBridge#116 env2 data-path soak (2026-11-12, record
docs/plans/2026-11-12-leader-auto-down-env2-drill.md§5, commits6c7e150a/644f564c).What happened. LocalDb replication was turned on for the env2 site-x pair, whose two per-node stores had diverged. Both nodes were recreated together. The node that came up active loaded its own stale local data about 1 s before the first sync snapshot arrived. Its startup reconcile then found nothing missing, so the deployed instances were never started on it. Re-pushing the artifacts and redeploying the two instances recovered it. Normal failovers on an already-replicated pair were not affected.
Why it matters. The QA pair and any site pair that gets replication enabled later go through this exact first-enable path. The symptom is silent: the cluster is healthy, but no instances run.
To do.
Fixed in
d7fa31f0(main).Root cause. The symptom was a delay of one sweep interval, not "never started". The instance reconcile sweep did heal it, but only after its plain 60 s cadence.
DeploymentManagerActor.HandleStartupConfigsLoaded). The peer's rows land by LocalDb sync about 1 s later.SiteReconciliationActor, at +5 s) rightly reports 0 gaps, because the rows are already in the store when it asks central. So it sends noEnsureInstanceStartedCommand.HandleInstanceSweepTick, register row 78). Its timer is armed inPreStartatInstanceSweepIntervalSeconds(60 s). Before the fix, the first useful tick came one full interval after the boot load.deploy-artifacts+ redeploy at 04:43:43–45. That is about 49 s after the 04:42:54 boot load, so it got in just before the first sweep tick at 04:43:54. That tick then also restartedMxAlarmDemo-1from its stale revision ("running stale revision … deployment-manager sweep self-heal"). Without the redeploy, the sweep would have started the two instances at about +60 s.Rig reproduction (primary rig, site-a pair, image before the fix).
zz135-probe(lands only on site-a-b), stopped site-a-b, then started both together.05:03:16 Loaded 10 deployed configs(probe missing),05:03:21 Reconcile pass 1/3 … 0 fetched.05:04:16 Instance zz135-probe started by deployment-manager sweep self-heal. That is a 60 s gap.Fix. Post-boot settle window in the DM singleton (
MarkStartupInstanceCreationComplete):ScadaBridge:SiteRuntime:InstanceSweepSettleIntervalSeconds(default 5) forInstanceSweepSettleWindowSeconds(default 120). After that it falls back toInstanceSweepIntervalSeconds.>= 0, and0disables the settle phase. Nothing extra runs when the sweep itself is disabled.ISyncStatus.LastSyncUtcis also bumped on snapshot send), so the fix uses the sweep, which does not depend on how the rows arrived. If an exact trigger is wanted later, a lib-sideSnapshotsReceivedcounter or event onISyncStatuswould be the clean hook. The settle sweep doesn't need it.Tests.
PostBootSettleSweepTests(4 tests). Every test pins the plain interval at 60 s, so a pass can only come from the settle path. There is also a validator theory.Rig, with the fix (same scenario,
zz135-probe2, site-a pair recreated together on the new image):05:11:18 Loaded 11 deployed configs(probe2 missing)05:11:19 All 11 Instance Actors created05:11:19 Instance zz135-probe2 started by deployment-manager sweep self-heal: within the same second, and before the startup reconcile at 05:11:23.Both probe instances were deleted afterwards. site-a-a and site-a-b are Healthy,
memberCount 2, replication connected, backlog 0. The env2 stack andscadabridge:env2-116were not touched.Side observation, not part of this fix. With site-a-a stopped, the first
MgmtDeployInstanceto site-a returnedOUTCOME_UNKNOWN: connection failed after the command may have been applied. An immediate retry succeeded against site-a-b. This happened on both attempts.