storage: pin session rendering GUCs on Postgres source connections - #38745
Conversation
Set DateStyle, IntervalStyle, TimeZone, and extra_float_digits in the startup options of every connection made through mz_postgres_util::Config and verify them after connecting. Before-image text otherwise follows upstream session defaults that can change mid-life, breaking retraction symmetry for TEXT COLUMNS values. Closes: SS-362 Closes: SS-376 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
QA LLM Review1. HIGH -- Pinning the GUCs breaks retraction symmetry for rows already ingested by existing sources
Applying the pins to every connection immediately changes the text rendering used for before-images of sources that were snapshotted before the pins existed. For a source with DetailsThe PR description states pre-pin rows still retract cleanly because the pins are instant-preserving. That holds for Concretely, upstream
The new Suggested fix: make the rendering settings a per-source property rather than a global connection property, so a source only changes rendering at a re-snapshot boundary. Recording the pinned set on Note that single-version testdrive cannot reproduce this, since with the pins in place the snapshot is pinned too. A platform check that ingests under a non-UTC upstream default in the old version and updates after the upgrade is the shape that would cover it. |
|
Closing. The QA finding is correct: TEXT COLUMNS store rendered text verbatim, so a connection-level pin changes the retraction form for every existing source with a non-default upstream, and the first UPDATE after upgrade leaves a permanent phantom row. Any global pin, including DateStyle alone, has this shape. If the pin is ever pursued it has to be a per-source property fixed at CREATE time. The four-leg repro in this PR stands on its own and can be lifted into that work. |
Pin the PostgreSQL session settings that control text rendering on every connection a Postgres source makes, and fail the connection if the server does not honor them.
Motivation
Source ingestion parses the text PostgreSQL renders, both in COPY snapshots and in the before-images of the replication stream, and a retraction only cancels its insertion if both were rendered identically. That rendering follows
DateStyle,IntervalStyle,TimeZone, andextra_float_digits, which a source connection inherits from server or database defaults the customer can change at any point in a source's life. A change picked up by a fresh walsender, after a managed failover, a parameter-group apply, orpg_terminate_backend, renders the before-image differently from the stored row, and full-row reads fail withNon-positive multiplicity in DistinctBy, the signature of incident 1161. SS-376 confirmed the repro on the TimeZone leg with atimestamptzinside aTEXT COLUMNScomposite.Change
mz_postgres_util::Configgains aPINNED_SESSION_SETTINGSconstant (DateStyle=ISO,IntervalStyle=postgres,TimeZone=UTC,extra_float_digits=3).Config::newappends them to the startupoptionsstring, which already carrieswal_sender_timeoutand so is known to reach the walsender. After each connect, the settings are read back withcurrent_settingover the simple-query protocol, and a mismatch fails the connection naming the setting and quoting what the server reported. That turns a pooler configured to strip startup options into a loud connection error instead of a silent rendering difference.PostgresConnection::configin storage-types is the only constructor of thisConfig, so snapshot COPY, replication, and purification connections all get the pins. No call site changes.Pins are instant-preserving for rows ingested before the pin: a
timestamptzrendered as17:15:00+02under Europe/Berlin and as15:15:00+00under UTC parses to the same datum, so pre-pin rows still retract cleanly. Any positiveextra_float_digitsselects shortest round-trip float output.Tests
test/pg-cdc/session-guc-change.tdis the SS-376 repro extended to one leg per pinned setting. Each leg changes the setting at the database level, terminates the walsender so the next change is decoded by a fresh session, updates a row, and reads the table back through operators with multiplicity checks. It ends with an assertion on the exact rendered composite text and resets the database-level settings, which outlive the file. Without the pins each leg fails with the DistinctBy error: verified by running with progressively more pins enabled, so every pin has a leg that fails when it alone is removed. With all four pins the file passes, and so does the rest of the pg-cdc suite.Suggested review order
src/postgres-util/src/tunnel.rs: the constant and its doc comment, thenConfig::new, thenverify_pinned_session_settings.test/pg-cdc/session-guc-change.td.Release note
This release will pin
DateStyle,IntervalStyle,TimeZone, andextra_float_digitson PostgreSQL source connections, so upstream session-default changes can no longer corrupt ingested rows. Connections through a pooler that strips startup options now fail with a clear error.Closes: SS-362
Closes: SS-376
🤖 Generated with Claude Code