From c110ae2443a559b6ebe6447946718b5068f921e5 Mon Sep 17 00:00:00 2001 From: Mason Sharp Date: Fri, 17 Apr 2026 09:25:28 -0700 Subject: [PATCH 1/3] Prepare v1.9.0 --- docs/CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 001ebe6..be5b987 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to ACE will be captured in this document. This project follows semantic versioning; the latest changes appear first. +## [v1.9.0] - 2026-04-17 + +### Added +- `--until` flag for `mtree` build/diff commands to bound operations by commit timestamp. + +### Changed +- Switched SQLite driver from cgo `mattn/go-sqlite3` to pure-Go `modernc.org/sqlite`. Enables fully static binaries on every platform and fixes a latent bug where darwin/windows builds silently dropped the sqlite driver at runtime. + +### Fixed +- `table-diff` could OOM when a node became unresponsive mid-run. Workers had no way to stop and would grind through every remaining sub-range (each waiting ~60 s for context deadline), accumulating errors and log output. Added a circuit breaker that short-circuits all workers — including the initial hash phase — as soon as any node error is recorded. Backed by `atomic.Bool` to avoid mutex overhead on the common path. +- `--until` correctly handles frozen rows (NULL commit timestamps after freeze). +- Merkle tree row-hash and fetch-rows queries now quote identifiers via `pgx.Identifier.Sanitize()` instead of interpolating raw qualified names. +- Spock origin filter is validated as an integer via `strconv.Atoi` rather than string-escaped; non-numeric values are rejected. + +### Security +- Go directive bumped 1.25.4 → 1.26.0; release builds use Go 1.26.2, resolving stdlib CVEs including CVE-2025-68121 (CRITICAL). +- Upgraded `moby/buildkit` v0.27.1 → v0.28.1 (CVE-2026-33747, CVE-2026-33748). +- Upgraded `go.opentelemetry.io/otel/sdk` + exporters to v1.43.0 (CVE-2026-39882, CVE-2026-39883). +- Upgraded `google.golang.org/grpc` v1.79.1 → v1.80.0 (CVE-2026-33186). +- Switching to distroless/static-debian12 removes libc6 and libssl3 from the image entirely, eliminating the class of CVEs reported against those packages (including CVE-2025-27587). +- GitHub Actions pinned to commit SHAs; `goreleaser` image pinned to v2.15.2. + ## [v1.8.1] - 2026-04-10 ### Changed From a1a87d6bbdb1260dd8441dfc0ab5077806cd3271 Mon Sep 17 00:00:00 2001 From: Mason Sharp Date: Fri, 17 Apr 2026 11:12:21 -0700 Subject: [PATCH 2/3] fix: propagate server-side errors from logical replication stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit processReplicationStream fell through to the default case on *pgproto3.ErrorResponse, logging only the Go type and continuing the receive loop. The next ReceiveMessage would then time out and the code would falsely report "Replication stream drained" — so UpdateFromCDC returned nil, callers assumed success, and metadata was updated with an LSN that hadn't actually advanced. Add an explicit case for *pgproto3.ErrorResponse that logs every server diagnostic field (severity, code, message, detail, hint, where, routine) and treats the event as fatal: sets processingErr, stops streaming, and returns the SQLSTATE + message to the caller as a real error. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/infra/cdc/listen.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/infra/cdc/listen.go b/internal/infra/cdc/listen.go index 65e976f..16733fc 100644 --- a/internal/infra/cdc/listen.go +++ b/internal/infra/cdc/listen.go @@ -380,6 +380,11 @@ func processReplicationStream(ctx context.Context, nodeInfo map[string]any, cont stopStreaming = true } } + case *pgproto3.ErrorResponse: + logger.Error("replication stream aborted by server: severity=%s code=%s message=%q detail=%q hint=%q where=%q routine=%q", + msg.Severity, msg.Code, msg.Message, msg.Detail, msg.Hint, msg.Where, msg.Routine) + processingErr = fmt.Errorf("server aborted replication: %s (SQLSTATE %s)", msg.Message, msg.Code) + stopStreaming = true default: logger.Info("Received unexpected message: %T", msg) } From 201b2e2e447cad94e52fd3e424fc602d4fd09ca7 Mon Sep 17 00:00:00 2001 From: Mason Sharp Date: Fri, 17 Apr 2026 11:21:07 -0700 Subject: [PATCH 3/3] fix: close pgxpool in processReplicationStream to prevent connection leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit processReplicationStream acquired a pgxpool via auth.GetClusterNodeConnection on entry and never closed it — no defer, no cleanup on any return path. Every call to UpdateFromCDC or ListenForChanges leaked one pool, and each leaked pool retains a backgroundHealthCheck goroutine plus its connections. Under repeated invocations (e.g. the CDC regression tests with -count=10) leaked connections accumulated until Postgres refused new clients with "sorry, too many clients already" (SQLSTATE 53300). Add the defer immediately after the pool acquisition so all return paths release it, including the long-running continuous ListenForChanges case. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/infra/cdc/listen.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/infra/cdc/listen.go b/internal/infra/cdc/listen.go index 16733fc..0038dae 100644 --- a/internal/infra/cdc/listen.go +++ b/internal/infra/cdc/listen.go @@ -56,6 +56,7 @@ func processReplicationStream(ctx context.Context, nodeInfo map[string]any, cont logger.Error("failed to get connection pool: %v", err) return fmt.Errorf("failed to get connection pool: %w", err) } + defer pool.Close() processingCtx := context.WithoutCancel(ctx) mtreeCfg := config.Get().MTree