Case 1: different column type on publisher and subscriber ----------------------------------------------------------- The publisher's text value is about 590kB. The subscriber reads it as numeric[] (each 1e-16383 prints 16,386 characters), about 1.07GB. -- [PUB] insert before subscribing, so the row never exists on the subscriber CREATE TABLE ttyp (k text); ALTER TABLE ttyp REPLICA IDENTITY FULL; INSERT INTO ttyp SELECT '{' || repeat('1e-16383,', 65599) || '1e-16383}'; CREATE PUBLICATION pub_typ FOR TABLE ttyp; -- [SUB] CREATE TABLE ttyp (k numeric[] PRIMARY KEY); CREATE SUBSCRIPTION sub_typ CONNECTION 'port=9933 dbname=postgres' PUBLICATION pub_typ WITH (copy_data = false, conflict_log_destination = 'table'); -- [PUB] triggers delete_missing on the sub DELETE FROM ttyp; -- [SUB] expect in sub.log: ERROR: invalid memory alloc request size 1074921602 LOG: conflict detected on relation "public.ttyp": conflict=delete_missing DETAIL: Conflict details are logged to the conflict log table: pg_conflict.pg_conflict_log_16410 CONTEXT: processing remote data for replication origin "pg_16410" during message type "DELETE" for replication target relation "public.ttyp" in transaction 675, finished at 0/017CCD60 ERROR: invalid memory alloc request size 1074921602 -- Cleanup -- [SUB] DROP SUBSCRIPTION sub_typ; DROP TABLE ttyp; -- [PUB] DROP PUBLICATION pub_typ; DROP TABLE ttyp; Case 2: binary = true (same types on both sides) --------------------------------------------------- The publisher sends numeric[] in binary form, about 115kB. The subscriber renders it as text (each 1e131071 prints 131,073 digits), about 1.07GB. -- [PUB] insert before subscribing, so the row never exists on the subscriber CREATE TABLE tbin (k numeric[]); ALTER TABLE tbin REPLICA IDENTITY FULL; INSERT INTO tbin SELECT ARRAY(SELECT 1e131071::numeric FROM generate_series(1, 8200)); CREATE PUBLICATION pub_bin FOR TABLE tbin; -- [SUB] CREATE TABLE tbin (k numeric[] PRIMARY KEY); CREATE SUBSCRIPTION sub_bin CONNECTION 'port=9933 dbname=postgres' PUBLICATION pub_bin WITH (copy_data = false, binary = true, conflict_log_destination = 'table'); -- [PUB] triggers delete_missing on the subscriber DELETE FROM tbin; -- [SUB] expect in sub.log: ERROR: invalid memory alloc request size ... LOG: conflict detected on relation "public.tbin": conflict=delete_missing DETAIL: Conflict details are logged to the conflict log table: pg_conflict.pg_conflict_log_16392 CONTEXT: processing remote data for replication origin "pg_16392" during message type "DELETE" for replication target relation "public.tbin" in transaction 668, finished at 0/017C1548 ERROR: invalid memory alloc request size 1074798602 -- Cleanup -- [SUB] DROP SUBSCRIPTION sub_bin; DROP TABLE tbin; -- [PUB] DROP PUBLICATION pub_bin; DROP TABLE tbin; Case 3: bytea_output differs on the subscriber --------------------------------------------------- The key is a bytea nested in 16 composite levels, about 250 bytes stored. In hex (the publisher's form) it renders to about 413kB. In escape, every byte gets a backslash, and each nesting level doubles the backslashes, so the text goes past 1GB. -- [PUB] and [SUB] same types and table on both nodes DO $$ BEGIN CREATE TYPE b0 AS (f bytea); FOR i IN 1..16 LOOP EXECUTE format('CREATE TYPE b%s AS (f b%s)', i, i - 1); END LOOP; END $$; CREATE TABLE tbyt (k b16 PRIMARY KEY); -- [PUB] CREATE PUBLICATION pub_byt FOR TABLE tbyt; -- [SUB] CREATE SUBSCRIPTION sub_byt CONNECTION 'port=9933 dbname=postgres' PUBLICATION pub_byt WITH (copy_data = false, conflict_log_destination = 'table'); -- [PUB] replicates normally DO $$ DECLARE s text := 'ROW(decode(repeat(''80'', 10000), ''hex''))::b0'; BEGIN FOR i IN 1..16 LOOP s := format('ROW(%s)::b%s', s, i); END LOOP; EXECUTE 'INSERT INTO tbyt VALUES (' || s || ')'; END $$; -- [SUB] row arrived; switch the apply worker to escape and remove the row locally SELECT count(*) FROM tbyt; -- 1 (don't SELECT k) ALTER SYSTEM SET bytea_output = 'escape'; SELECT pg_reload_conf(); DELETE FROM tbyt; -- [PUB] triggers delete_missing on the subscriber DELETE FROM tbyt; -- [SUB] expect in sub.log: ERROR: string buffer exceeds maximum allowed length LOG: parameter "bytea_output" changed to "escape" LOG: conflict detected on relation "public.tbyt": conflict=delete_missing DETAIL: Conflict details are logged to the conflict log table: pg_conflict.pg_conflict_log_16475 CONTEXT: processing remote data for replication origin "pg_16475" during message type "DELETE" for replication target relation "public.tbyt" in transaction 682, finished at 0/017F28C8 ERROR: string buffer exceeds maximum allowed length (1073741823 bytes) DETAIL: Cannot enlarge string buffer containing 1073741822 bytes by 1 more bytes. -- [SUB] control: back to hex, retry; the row is recorded with an omitted marker ALTER SYSTEM RESET bytea_output; SELECT pg_reload_conf(); SELECT conflict_type, has_omitted_values FROM :clt; -- delete_missing | t -- Cleanup -- [SUB] DROP SUBSCRIPTION sub_byt; DROP TABLE tbyt; DROP TYPE b0 CASCADE; -- [PUB] DROP PUBLICATION pub_byt; DROP TABLE tbyt; DROP TYPE b0 CASCADE; Case 4: quote_all_identifiers differs on the subscriber ---------------------------------------------------------- The key is a regclass[] of 2,100 × pg_class, nested in 16 composite levels, about 230 bytes stored. With quotes off (the publisher's form) it renders to about 281kB. With quote_all_identifiers = on, every name gets quotes, each nesting level doubles them, and the text goes past 1GB. -- The array names a catalog table on purpose. The apply worker uses search_path = '', so an unqualified user table name would fail to resolve on the subscriber. -- [PUB] and [SUB] same types and table on both nodes DO $$ BEGIN CREATE TYPE r0 AS (f regclass[]); FOR i IN 1..16 LOOP EXECUTE format('CREATE TYPE r%s AS (f r%s)', i, i - 1); END LOOP; END $$; CREATE TABLE tqai (k r16 PRIMARY KEY); -- [PUB] CREATE PUBLICATION pub_qai FOR TABLE tqai; -- [SUB] CREATE SUBSCRIPTION sub_qai CONNECTION 'port=9933 dbname=postgres' PUBLICATION pub_qai WITH (copy_data = false, conflict_log_destination = 'table'); -- [PUB] replicates normally DO $$ DECLARE s text := 'ROW(ARRAY(SELECT ''pg_class''::regclass FROM generate_series(1, 2100)))::r0'; BEGIN FOR i IN 1..16 LOOP s := format('ROW(%s)::r%s', s, i); END LOOP; EXECUTE 'INSERT INTO tqai VALUES (' || s || ')'; END $$; -- [SUB] row arrived; switch the apply worker to quoting and remove the row locally SELECT count(*) FROM tqai; -- 1 ALTER SYSTEM SET quote_all_identifiers = on; SELECT pg_reload_conf(); DELETE FROM tqai; -- [PUB] triggers delete_missing on the subscriber DELETE FROM tqai; -- [SUB] expect in sub.log: ERROR: string buffer exceeds maximum allowed length LOG: parameter "quote_all_identifiers" changed to "on" LOG: conflict detected on relation "public.tqai": conflict=delete_missing DETAIL: Conflict details are logged to the conflict log table: pg_conflict.pg_conflict_log_16540 CONTEXT: processing remote data for replication origin "pg_16540" during message type "DELETE" for replication target relation "public.tqai" in transaction 690, finished at 0/01817028 ERROR: string buffer exceeds maximum allowed length (1073741823 bytes) DETAIL: Cannot enlarge string buffer containing 1073741822 bytes by 1 more bytes. -- [SUB] control: back to the default, retry; the row is recorded with an omitted marker ALTER SYSTEM RESET quote_all_identifiers; SELECT pg_reload_conf(); SELECT conflict_type, has_omitted_values FROM :clt; -- delete_missing | t -- Cleanup -- [SUB] DROP SUBSCRIPTION sub_qai; DROP TABLE tqai; DROP TYPE r0 CASCADE; -- [PUB] DROP PUBLICATION pub_qai; DROP TABLE tqai; DROP TYPE r0 CASCADE;