chore(deps): bump ZB.MOM.WW.LocalDb 0.1.3 -> 0.2.1, correct the MaxBatchSize rationale
Closes the pin skew against ScadaBridge (which already pins 0.2.1). Pure pin
bump — no code change was required:
- `RegisterReplicated(string)` still delegates to `baselineExistingRows: false`,
so `LocalDbSetup.OnReady` is byte-identical in behaviour.
- Everything 0.2.x added is additive (`DeregisterReplicated`, `MaxBatchBytes`,
the `baselineExistingRows` overload); none of it is called here.
- The wire protocol is unchanged (0.1.x peers still sync) and the on-disk
bookkeeping schema v1 -> v2 upgrades in place on open, so no rig, volume or
operator action is needed.
The one thing that DID need reconciling is documentation. 0.2.0 made batching
byte-budgeted: `MaxBatchBytes` (2 MB default, under the 4 MB gRPC cap) now
bounds the wire message, and `MaxBatchSize` is demoted to a secondary row cap
plus the DB read-page size. The rig's `MaxBatchSize = 16` was chosen as a
hand-computed byte proxy ("row-count-only against gRPC's 4 MB cap"), and that
rationale is now wrong everywhere it appeared — appsettings, Configuration.md,
the pair-replication runbook, and both docker-dev site-a services.
The VALUE stays 16, for a different and still-real reason: OplogStore /
SnapshotStreamer materialise a whole read page in memory *before* the
byte-budget split runs, so 16 x ~171 KB artifact chunk rows bounds that
transient allocation at ~2.7 MB. Documented as such, with `MaxBatchBytes`
added to both config tables.
CLAUDE.md records the pin and two deliberate non-adoptions: this repo does not
take ScadaBridge's conditional-registration + boot-time `DeregisterReplicated`
self-heal (OnReady registers all four tables unconditionally, and
LocalDbSetupTests pins that set), and consequently does not use
`baselineExistingRows` either — so enabling replication on a long-running
default-OFF node converges on subsequent writes, not on rows already in the
file. Both are behaviour changes, out of scope for a pin bump.
Verification: solution builds clean; full suite run (57 projects). All 66
LocalDb tests in Host.IntegrationTests green. One unrelated failure,
RoslynVirtualTagEvaluatorTests.Evaluate_racing_ClearCompiledScripts_never_
fails_with_disposed — a Roslyn evaluator disposal race with no LocalDb in its
path, whose own comment documents that its "at most one clear per evaluation"
assumption depends on thread-pool pressure. It passed 3/3 re-runs in isolation
and fails only under full-solution parallel load; pre-existing test-design
fragility, not a regression from this bump.
This commit is contained in:
@@ -578,6 +578,36 @@ same event accepted on both nodes — which happens in every boot window, since
|
||||
docker-dev rig the site-a pair and site-b both run `AlarmHistorian__Enabled=true` against a
|
||||
deliberately unresolvable gateway endpoint, so the buffer is always a live, non-draining queue.
|
||||
|
||||
**The library pin is `0.2.1`** (`Directory.Packages.props`, both `ZB.MOM.WW.LocalDb` and
|
||||
`.Replication`), bumped from `0.1.3` on 2026-08-15. It was a **pure pin bump — no code change**:
|
||||
`RegisterReplicated(string)` is unchanged, everything 0.2.x added is additive, the wire protocol is
|
||||
untouched (0.1.x peers still sync), and the on-disk bookkeeping schema v1 → v2 upgrades in place on
|
||||
open, so no rig or volume action was needed. Three things about it are worth knowing:
|
||||
|
||||
- **`MaxBatchSize` no longer means what the rig's `16` was chosen to mean.** 0.2.0 made batching
|
||||
**byte-budgeted** — `LocalDb:Replication:MaxBatchBytes` (2 MB default, left unset here, sized
|
||||
under the 4 MB gRPC cap) bounds the wire message. `MaxBatchSize` is demoted to a secondary row
|
||||
cap **and** the DB read-page size in `OplogStore.ReadBatchAboveAsync`/`SnapshotStreamer`, which
|
||||
materialise a whole page in memory *before* the byte split runs. So the site-a pair keeps
|
||||
`16` — it now bounds that transient allocation (≈2.7 MB at ≈171 KB artifact chunk rows), not the
|
||||
message size. Every comment and doc row that called it "row-count-only against gRPC's 4 MB cap"
|
||||
was corrected with the bump; do not restore that rationale.
|
||||
- **`DeregisterReplicated` exists and this repo deliberately does not call it.** `LocalDbSetup.OnReady`
|
||||
registers all four tables **unconditionally**, on every driver node, replicating or not — a
|
||||
replication-configured node and a default-OFF one (central, site-b) carry identical CDC triggers.
|
||||
ScadaBridge went the other way (conditional registration + a boot-time deregister self-heal), so
|
||||
do not read that pattern across as settled here: it would be a behaviour change, and
|
||||
`LocalDbSetupTests` pins the registered-table set and the DDL→register ordering exactly. The
|
||||
cost of the current shape is three unused triggers per table on an unreplicated node; the
|
||||
benefit is that turning replication on is a config change that needs no re-registration pass.
|
||||
- **Consequently, `baselineExistingRows` is not used either.** Rows written on a node *before*
|
||||
replication is enabled are not in `__localdb_row_version`, so enabling replication on a
|
||||
long-running default-OFF node (central, site-b) converges on subsequent **writes**, not on the
|
||||
rows already in the file. Passing `RegisterReplicated(table, baselineExistingRows: true)` is the
|
||||
library's answer, but it flags a snapshot resync and is only correct when gated on replication
|
||||
actually being configured — which the unconditional registration above cannot express. Enable
|
||||
replication on a fresh pair, or accept the gap and let the next deploy re-cache.
|
||||
|
||||
See `docs/Configuration.md` (`LocalDb` section), `docs/Redundancy.md` (pair-local config
|
||||
cache), `docs/AlarmHistorian.md`, and the runbook
|
||||
`docs/operations/2026-07-20-localdb-pair-replication.md`.
|
||||
|
||||
Reference in New Issue
Block a user