# v0.69.0 Full Details — DuckLake Sink Reliability & Security > **Summary:** [v0.69.0.md](v0.69.0.md) > **Assessment source:** [plans/PLAN_OVERALL_ASSESSMENT_13.md](../plans/PLAN_OVERALL_ASSESSMENT_13.md) --- ## Motivation After v0.68.0 fixes the most urgent correctness defects, v0.69.0 focuses entirely on the DuckLake integration surface. The assessment identified five gaps that cluster around sink reliability and schema safety: - ARCH-002 / REL-001: sink failures are invisible and unretried — data-lake delivery is not trustworthy. - COR-005: view registration is stale after query-only ALTER — DuckLake clients see the old view definition. - COR-006: snapshot IDs are allocated without locking — concurrent sinks could collide. - SEC-002: schema resolution is unqualified — `search_path` manipulation can misdirect catalog writes. - OBS-001: no health metrics — operators cannot detect degraded sink delivery. --- ## Detailed Implementation ### ARCH-002 / REL-001: Sink Delivery State Machine **New catalog object:** ```sql CREATE TABLE pgtrickle.pgt_ducklake_sink_delivery ( delivery_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, stream_table_id oid NOT NULL REFERENCES pgtrickle.pgt_stream_tables(pgt_id), refresh_id bigint, status text NOT NULL CHECK (status IN ('PENDING','WRITING','DELIVERED', 'FAILED_RETRYABLE','FAILED_PERMANENT')), attempt_count int NOT NULL DEFAULT 0, bytes_written bigint, rows_written bigint, started_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz, last_error text ); CREATE INDEX ON pgtrickle.pgt_ducklake_sink_delivery (stream_table_id, started_at DESC); ``` **New GUC:** - `pg_trickle.ducklake_sink_max_retries` (int, default 3): maximum `FAILED_RETRYABLE` attempts before transitioning to `FAILED_PERMANENT`. - `pg_trickle.ducklake_sink_failure_mode` (enum `warn|error`, default `warn`): whether a `FAILED_PERMANENT` row propagates as a PostgreSQL error. **Files changed:** - `sql/pg_trickle--0.68.0--0.69.0.sql` — add table, indexes, GUC docs - `src/ducklake_sink.rs` — rewrite `run_ducklake_sink()` as state machine - `src/config.rs` — add two new GUCs - `src/shmem.rs` — add DuckLake sink Prometheus counters - `tests/e2e_ducklake_tests.rs` — tests for retry, permanent failure, status function --- ### COR-005: View Registration on Query-Only ALTER **Files changed:** - `src/api/alter.rs` — after `alter_stream_table_query()` returns `Ok`, check `st.ducklake_sink_mode.is_some()` and if true call `register_ducklake_view()` with the updated query - `tests/e2e_ducklake_tests.rs` — `test_ducklake_sink_view_updates_on_query_alter` --- ### COR-006: Snapshot ID Advisory Lock **Files changed:** - `src/ducklake_sink.rs` — `register_ducklake_data_file()`: add `Spi::run("SELECT pg_advisory_xact_lock($1)", &[hashtext_of(table_id)])` before `MAX(snapshot_id)` computation - `tests/e2e_ducklake_tests.rs` — `test_ducklake_sink_concurrent_snapshot_ids` (two stream tables pointing at the same `ducklake_sink_table_id`, concurrent refresh, no duplicate `snapshot_id`) --- ### SEC-002: Qualified Schema Resolution **Files changed:** - `src/config.rs` — add `pg_trickle.ducklake_catalog_schema` GUC - `src/ducklake_sink.rs` — `ducklake_view_table_exists()` queries `pg_class JOIN pg_namespace`; all INSERT/UPDATE statements use `format!("{}.ducklake_view", quote_ident(schema))` - `docs/GUC_CATALOG.md` — document `ducklake_catalog_schema` - `docs/CONFIGURATION.md` — cross-reference DuckLake schema setup --- ### OBS-001: Sink Health Metrics **New SQL function:** ```sql CREATE OR REPLACE FUNCTION pgtrickle.ducklake_sink_status() RETURNS TABLE ( stream_table_name text, last_delivery_status text, last_delivery_at timestamptz, last_bytes_written bigint, last_rows_written bigint, failed_attempts int, last_error text ) LANGUAGE sql STABLE SECURITY DEFINER ...; ``` **Files changed:** - `src/monitor/mod.rs` — add `ducklake_sink_status()` pg_extern - `src/shmem.rs` — five new atomic counters per-sink - `monitoring/dashboards/pg_trickle_ducklake.json` — add DuckLake sink delivery panel to the Grafana dashboard (or create new dashboard) --- ## Upgrade Notes The new `pgt_ducklake_sink_delivery` table is created by the migration script. Existing deployments will have no rows in it until the first post-upgrade DuckLake sink refresh. No data loss occurs during upgrade. The `ducklake_catalog_schema` GUC defaults to `main`; if your DuckLake installation uses a different schema name, set the GUC before the first post-upgrade refresh. --- ## Implementation Status | ID | Title | Status | |----|-------|--------| | ARCH-002 / REL-001 | Sink delivery state machine with retry/backoff | ✅ Done | | COR-005 | View registration on query-only ALTER | ✅ Done | | COR-006 | Snapshot ID advisory lock | ✅ Done | | SEC-002 | Qualified schema resolution (`ducklake_catalog_schema` GUC) | ✅ Done | | OBS-001 | Sink health metrics (`ducklake_sink_status()` SQL function) | ✅ Done | | DEP-002 | Dependency policy docs (`docs/DEPENDENCIES.md`) | ✅ Done | --- ## Exit Criteria - [x] All P0 items ✅ Done - [x] `just test-unit` passes - [x] `just lint` passes with zero warnings - [x] `just check-version-sync` exits 0 - [x] CHANGELOG.md entry written - [ ] ROADMAP.md v0.69.0 row marked ✅ Released