sluice v0.100.0
Milestone — the 0.99.x line has become an audited, gate-ratcheted, field-validated core. This is the confidence release, not v1.0.0 and not a stability guarantee: the version bump marks the point where the evidence is worth pointing at, and it is the release to start evaluating against. Drop-in upgrade, no breaking changes, no flag changes — the only new runtime behaviors are advisory WARNs (off-default or read-time), so nothing on the data path moves. If you are assessing sluice for a migration or a continuous sync, start at the new consolidated docs/production-readiness.md: support matrix, CDC-mode-per-source table, an honest Known-Limitations list, and a pre-production checklist.
Why this is a milestone
v0.100.0 is a confidence milestone rather than a feature drop, and the evidence is assembled, not asserted (the full record is committed at docs/dev/notes/v0100-milestone-validation.md):
- Standing gates on every commit that reached this tag. Six required real-DB integration checks run on every PR/push — cross-engine migrate + sync + backup against real MySQL, Postgres, and Vitess containers under the race detector, sharded behind a can't-rot coverage guard, with the VStream silent-loss class as its own required check — plus a tag-time real-Vitess-cluster publish gate (the
TestVitessClusterFilteredSyncfiltered move-OUT crux family) that must report green before this release can be published. - Three fully-remediated blind audits in the ten days before the cut (2026-07-15, 07-19, 07-23). Every finding became a permanent deterministic gate — the Tier-1 ratchet — so no audited class needs re-finding, and no silent-loss class is open. Both the demand-gated tail (index-DDL forward, trigger carry, warehouse targets, the translator-rule set) and the explicitly-carried residuals are documented with workarounds rather than implied.
- An all-green extended-suites dispatch of the classes that are not in the per-PR gates — the NOBLOB belt, VStream pipeline, real-world corpus, KMS localstack, Vitess chaos, and the MySQL DDL-fixture corpus.
- New consolidated
docs/production-readiness.md— support matrix, CDC modes per source, a candid Known-Limitations section, and a pre-production checklist, so the honest maturity posture is in one place. - Live cloud-KMS validation this release (2026-07-24). Real Azure Key Vault sign/verify passed every cell — including the N-9 rotation-recovery crux, where a chain wrapped under key version N restores clean with an unversioned operator URL after the key is rotated to N+1 (rebind targets the recorded wrap-time version), with the negative control failing loudly — and real GCP Cloud KMS asymmetric-sign +
GetPublicKeyverify held live (the DER→raw signature conversion), alongside the standing AWSkmsverifyCI leg. Throwaway vault and RG were deleted and purged.
The maturity posture is candid, and now stated concretely in the README's new "Maturity, concretely" section: strong per-PR gates, real-PlanetScale 5M-row filtered-sync validation, and a multi-week soak fleet on live managed databases — set honestly against the gap that no stranger has yet run sluice against a production workload nobody wrote a test for first. That is why the tool refuses loudly rather than guessing when it is uncertain.
Features
Large /metrics reference plus a doc-sync gate. The 27 sluice_* Prometheus series are now documented in full in running-as-a-service.md, pinned against the real scrape by a doc-sync test so the table cannot drift from the exported metrics. sluice diagnose discoverability, a PlanetScale pre-release validation checklist, and ADR sidecar-status hygiene round out the operator-facing documentation this release.
Fixed
The relaxed-sql_mode clamp WARN now covers the MySQL CDC apply path (v0.100 readiness C2; off-default). Under the operator's opt-in --mysql-sql-mode='', a steady-state CDC INSERT/UPDATE whose value the target silently clamps or truncates (300 → 127 on a TINYINT) previously had no signal at all — the exact class the v0.99.28 SHOW WARNINGS closure caught for the three bulk-copy writers, but which never covered change_applier*.go, the path a long-running sync spends its life on. The apply-path probe is now the twin of the bulk one and is shaped for the hot path: strict sessions (the default) short-circuit before any round trip, so strict mode pays exactly zero added latency; relaxed sessions probe the same tx immediately after each value-writing statement — the serial single-row INSERT/UPDATE and the ADR-0139 coalesced multi-row upsert that the ADR-0104 concurrent lanes also emit — on the bulk path's sampling schedule, and tables already WARNed skip the probe entirely, so steady-state cost after the first WARN is zero. DELETE/TRUNCATE write no operator values and are not probed. Pinned RED-before-GREEN on real MySQL across both write shapes (the coalesced flush asserted non-vacuous via the multi-row flush seam), plus the strict-mode control still erroring loudly at the exec and the once-per-table dedup.
UNIQUE-constraint attribute weakening is no longer silent on Postgres sources (v0.100 readiness C3). A source constraint carrying NULLS NOT DISTINCT (PG 15+), DEFERRABLE, or WITHOUT OVERLAPS (PG 18+) lands on every sluice target — including same-engine PG — as a plain UNIQUE, which is strictly weaker: a NULLS NOT DISTINCT source begins admitting duplicate NULLs it used to reject, DEFERRABLE becomes immediate per-statement enforcement, and WITHOUT OVERLAPS stops rejecting overlapping periods. The PG schema reader now reads those attributes (version-gated on serverVersionNum so pre-15/pre-18 servers never hit a 42703), carries them as metadata on ir.Index with no emitter behavior change (the flags ride the backup/schema-history wire for the faithful-carry follow-up), and emits one WARN per affected constraint at schema-read time — the single chokepoint migrate, sync, backup, and preview all flow through — naming the attribute, the weaker landing, and a recreate-on-target hint. Migration still completes; this is a WARN, not a refusal. Implementation ground truth corrected a stale catalog assumption in the process: NULLS NOT DISTINCT lives on pg_index.indnullsnotdistinct, not a pg_constraint column. Faithful same-engine carry, and the newly-found plain-CREATE UNIQUE INDEX … NULLS NOT DISTINCT sibling gap, are filed follow-ups; the item-51 dump-parity oracle gained a cited allowlist cell so the weakening stays pinned and goes stale-loud when carry ships. Pinned RED-before-GREEN on real PG 16 plus a unit matrix covering the version gates and the attribute-naming table.
Apply-retry budget: a young Run whose first target outage strikes within one sentinel poll of start is now credited correctly (Bug 204; residual in the v0.99.292 apply-retry ledger — affects v0.99.292 only; loud, zero-loss). This was one of the residuals the v0.99.292 regression cycle found in the v0.99.291 Bug 202 fix. The v0.99.292 ledger's baseline only ever advanced inside counted-failure iterations, so an early first outage — within ~5s of Run start — could leave the baseline invalid forever, and a later outage's evidence had nothing to compare against, exiting apply retry budget exhausted despite verified durable progress in between (even when the fatal iteration itself held two successful anchor-bearing reads with different tokens). Two complementary, strictly evidence-based legs close it under the D0-4 discipline (a failed read is never evidence): within-iteration advancement — any two successful observations inside one iteration showing different tokens credit a fresh budget with no baseline at all — and baseline seeding from the Run's first successful anchor-bearing observation wherever it occurs. The loud floors are pinned unchanged at exactly N: a genuinely stuck batch (token frozen) and a target down from birth (no successful observation) both still exhaust in exactly the configured attempts. Pinned RED-before-GREEN by the compressed young-Run scripted-store sequence plus a resnapshot-iteration seeding cell.
Large-object stranding WARN: the named-suspect branch is now reachable (Bug 205; the v0.99.292 advisory's unreachable branch — affects v0.99.292 only; loud, zero-loss). The v0.99.292 large-object advisory ran after ReadSchema, but an in-scope oid/lo column — the very suspect shape the WARN names — is an unsupported column type that the schema read already refuses loudly (postgres: unsupported data_type "oid"), so the named branch was structurally dead code and only the quieter no-suspect WARN could ever fire. The census now runs before the schema read at both call sites (migrate's phaseReadSourceSchema and the sync cold start), with suspect scoping taken from the operator's table filter rather than the parsed schema, and the WARN wording was corrected to state what actually happens next: the schema read will refuse these columns — --exclude-table to proceed without them, convert to inline bytea via lo_get to carry the blobs, or export them separately (lo_export / pg_dump --blobs). This also corrects the v0.99.292 release notes' claim that in-scope referencing oid values "copy faithfully as plain integers" — they refuse, on both migrate and sync (the refusal is pre-existing and correct: oid is genuinely unsupported as a column type, and that behavior is deliberately unchanged). Pinned RED-before-GREEN by a real-PG end-to-end integration test.
Managed-Postgres resume robustness: the replication-slot-active retry is now a generous wall-clock budget (found by this release's live psverify run against PlanetScale-Postgres 18). When a CDC stream stops and restarts against a Postgres source, the old slot's walsender is released asynchronously by the server, and until it is, START_REPLICATION (and a slot drop / sync decommission) fails with SQLSTATE 55006 "replication slot is active". sluice retries that, but the budget was a fixed ~31.5 s — fine on self-hosted PG, too short for managed platforms: PlanetScale's upgrade of PS-Postgres to PG 18 pushed the walsender-release latency past ten minutes (proxy + wal_sender_timeout), so an immediate restart could exhaust the budget and fail loudly. The 55006 retry is now a generous five-minute wall-clock budget applied consistently across every site (the stream's START_REPLICATION and the decommission/drop paths, from one shared ir.SlotActiveReapBudget const), so a routine restart waits the release out; the disconnect-is-not-release semantics are preserved (a genuinely concurrent second writer still fails loudly after the budget — a longer budget only means slower loud failure on that misconfiguration, never masking). Loud and zero-loss throughout — the slot and position are intact; the retry only changes how long sluice waits before giving up. This surfaced because v0.100.0's milestone validation ran the live psverify suite for the first time since PlanetScale's PG 18 rollout; CDC from a PlanetScale-Postgres source is a narrow path no soak exercises, and nothing here was a sluice defect — the fix makes the managed-PG restart wait match the platform's reap window.
Compatibility
No breaking changes; drop-in upgrade, no flag changes. The only new runtime behaviors are advisory WARNs — the C2 relaxed-sql_mode clamp WARN fires only under the opt-in --mysql-sql-mode='' and adds zero cost to the default strict path, and the C3 UNIQUE-attribute WARN fires once per affected constraint at schema-read time — neither changes the data path or the exit code, and both are informational. The Bug 204 and Bug 205 fixes correct residuals in v0.99.292's own advisories and change no interface. The Azure Key Vault WARN wording was corrected (BadParameter versus the earlier "auth error" prediction) with behavior identical — the code already classified BadParameter; this was wording-only. VStream flavors, MySQL/MariaDB sources, and bulk-only runs are unaffected by the new WARNs.
Who needs this — action required
- Everyone evaluating sluice — this is the release to assess against. Start at
docs/production-readiness.mdfor the support matrix, CDC-mode-per-source table, Known-Limitations, and pre-production checklist; the milestone evidence record is atdocs/dev/notes/v0100-milestone-validation.md. - MySQL-target operators running CDC with
--mysql-sql-mode=''(relaxed sql_mode, opt-in) — you now get a one-time-per-table WARN on the apply path when the target silently clamps or truncates a steady-state CDC value. No data behavior changed; this is a new signal on a path that previously had none. Strict-mode targets (the default) are unaffected and pay no cost. - Postgres sources using
NULLS NOT DISTINCT,DEFERRABLE, orWITHOUT OVERLAPSUNIQUE constraints — you now get a heads-up WARN at schema read that the constraint lands as a plain (weaker) UNIQUE on the target. Migration still completes; recreate the attribute on the target if you need it (faithful carry is a filed follow-up). - Anyone whose stream exited
apply retry budget exhaustedearly on v0.99.292 during an outage very soon after the stream started — that was Bug 204; upgrade and restart the stream (warm resume — the exit was loud and lost nothing). - Everyone else: no action needed beyond upgrading.
Install
- Binaries: https://github.com/sluicesync/sluice/releases/tag/v0.100.0
- Homebrew:
brew install sluicesync/tap/sluice - Scoop:
scoop bucket add sluicesync https://github.com/sluicesync/scoop-bucket; scoop install sluice - winget:
winget install sluicesync.sluice - Docker:
docker pull ghcr.io/sluicesync/sluice:0.100.0
Full changelog: https://github.com/sluicesync/sluice/blob/main/CHANGELOG.md