Skip to content

deps: duckdb 1.5.5 + pyducklake 1.0.17 — leak-free ducklake channel, drift-gated - #119

Merged
jghoman merged 1 commit into
mainfrom
jakob/pyducklake-1017
Aug 13, 2026
Merged

deps: duckdb 1.5.5 + pyducklake 1.0.17 — leak-free ducklake channel, drift-gated#119
jghoman merged 1 commit into
mainfrom
jakob/pyducklake-1017

Conversation

@jghoman

@jghoman jghoman commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Why

The 1.5.2 extension channel is frozen at ducklake build 415a9ebd, which leaks per-connection native memory proportional to catalog metadata churn (A/B-verified in viaduck: ~1MiB per snapshot-advancing bind; OOM every 4-6h there). Millpond's long-lived writer connections against a busy shared catalog have the same exposure. The 1.5.5 channel serves d8a1881e, which is flat. pyducklake (ours) rides along as a deliberate lockstep canary — it hard-pins duckdb, so a one-sided bump of either package fails uv lock instead of silently splitting the two DuckLake stacks. Nothing imports it yet.

The drift gate

The frozen 1.5.2 channel was accidental drift protection; 1.5.5's channel is live and can re-serve new builds under the same URL. The release workflow runs in parallel with CI, so the test canary alone can't stop a drifted build from reaching the fleet via the mutable tag + autobounce. The Dockerfile now asserts the ducklake build SHA in the extension-install layer: an unexpected ducklake build cannot produce an image at all. Verified both directions (correct SHA builds; --build-arg DUCKLAKE_SHA=deadbeef fails the layer with the drift message).

Behavior changes under d8a1881e (all verified against real catalogs)

  • The (INT64_MAX, UINT64_MAX] shred-rejection window and write-time error signature are unchanged — the fix: fall back to string-only when the VARIANT write fails #118 guard and backstop hold. The inlined-path deferred failure now reports INT128 (was UINT64); the width-agnostic matcher still catches it.
  • JSON numbers outside [INT64_MIN, UINT64_MAX] in a VARIANT become lossy DOUBLE (were digit-preserving VARCHAR). Deliberate policy: follow native DuckLake semantics; the string column stays authoritative (now asserted). Rows written across the version boundary mix both representations in the companion permanently.
  • No catalog metadata migration: old/new builds read and write the same catalogs interchangeably (probed both directions, incl. shredded Parquet and inlined data), so mixed fleets and co-readers (viaduck, duckgres) are unaffected.
  • duckdb_databases() no longer lists the attach's metadata db as its own row — test-fixture-only; no prod code touches it.

New engine-behavior contract test

tests/integration/test_ducklake_engine_contract.py pins what the variant guard assumes of the pinned build, probed the way the 2026-08-12 incident executed (guard bypassed, real shredded Parquet writes): the containment invariant (engine-rejected ⊆ guard-rewritten, through the real sanitize_variant_sources chain), its converse (shreddable probes round-trip untouched), both window boundaries exactly, nested/array/sci-notation shapes, error-signature recognition, and the inlined commit-then-detonate path. The next bump that shifts any of this fails at the PR instead of on the fleet.

Supply chain

pyducklake 1.0.17 published 2026-08-13 → fixed-date exclude-newer-package carve-out from the 7-day cooldown (future releases re-enter it until deliberately bumped); sha256-pinned in uv.lock. Added to the dependency-review allow-list (PEP 639-only license metadata, same class as the opentelemetry entries; Apache-2.0).

Testing

  • 740 unit+integration tests pass, including the new 16 contract cases; e2e suite (4/4) against the built image; ruff clean.
  • Image built locally for arm64 with the gate; negative build verified to fail.
  • Cross-version probes run in side-by-side 1.5.2/1.5.5 environments (linux containers) for the mixed-fleet analysis.

Authored with agent assistance; two adversarial review passes (correctness + prod-impact/deployment) ran over the diff and their findings are incorporated (drift gate, allow-list entry, lockstep comment, nested/array probes, stale-comment sweep).

…drift-gated

The 1.5.2 extension channel is frozen at ducklake build 415a9ebd, which
leaks per-connection native memory proportional to catalog metadata
churn (A/B-verified in viaduck: ~1MiB per snapshot-advancing bind).
The 1.5.5 channel serves d8a1881e, which is flat — millpond's long-lived
writer connections against a busy shared catalog have the same exposure.
pyducklake (ours) rides along as a deliberate lockstep canary: it
hard-pins duckdb, so a one-sided bump of either package fails uv lock
instead of silently splitting the two DuckLake stacks. Published
yesterday, so it gets a fixed-date exclude-newer-package carve-out from
the 7-day cooldown.

Because the 1.5.5 channel is LIVE (the frozen channel was accidental
drift protection), the Dockerfile now asserts the ducklake build SHA in
the extension-install layer — an unexpected ducklake build cannot
produce an image at all. Verified both directions: correct SHA builds,
wrong SHA fails the layer. The release workflow runs in parallel with
CI, so the test canary alone could not stop a drifted build from
reaching the fleet via the mutable tag.

Behavior changes under d8a1881e, all verified against real catalogs:
- The (INT64_MAX, UINT64_MAX] shred-rejection window and its write-time
  error signature are UNCHANGED; the #118 guard and backstop hold. The
  inlined-path deferred failure now reports INT128 (was UINT64); the
  width-agnostic matcher still catches it.
- JSON numbers outside [INT64_MIN, UINT64_MAX] in a VARIANT become
  lossy DOUBLE (were digit-preserving VARCHAR). Deliberate policy:
  follow native DuckLake semantics; the string column stays
  authoritative (now asserted). Rows written across the version
  boundary mix both representations in the companion permanently.
- The ducklake attach no longer lists its metadata db as a separate
  duckdb_databases() row (test-fixture-only impact).
- No catalog metadata migration: old and new builds read/write the same
  catalogs interchangeably, so mixed fleets and co-readers (viaduck,
  duckgres) are unaffected.

New tests/integration/test_ducklake_engine_contract.py pins the engine
behaviors the variant guard assumes — the containment invariant
(engine-rejected values must be guard-rewritten, probed through the real
sanitize_variant_sources chain with the guard bypassed at the engine),
its converse (shreddable probes round-trip untouched), boundary values,
nested/array/sci-notation shapes, error-signature recognition, and the
inlined commit-then-detonate path — so the next bump that shifts any of
this fails at the PR instead of on the fleet.
@jghoman
jghoman merged commit 4e48d11 into main Aug 13, 2026
17 checks passed
@jghoman
jghoman deleted the jakob/pyducklake-1017 branch August 13, 2026 21:42
fuziontech added a commit that referenced this pull request Aug 14, 2026
Keep #119's pyducklake lockstep pin and the 1.5.5-strict VARIANT
DOUBLE assertions. Keep the PostHog fork overwrite, and --reinstall
the 1.5.5-posthog.2 wheel so uv does not leave the official
source_id (d8cdaa33fd) in the image. Runtime stage now asserts both
the fork source_id and ducklake SHA d8a1881e.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant