From 250f79564f5d40aa8e5cfa85a5f7276844278be9 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Fri, 30 Oct 2026 18:16:20 +0530 Subject: [PATCH v87 3/3] Document and comment that replica identity rendering can still exceed 1GB The 64kB cap on replica identity values is checked after a key value's text is produced, so in rare cases the text itself can exceed 1GB and conflict logging fails with an error. Mention this in the docs, and add an XXX comment in build_index_key_json() listing when it can happen. --- doc/src/sgml/logical-replication.sgml | 18 +++++++++++++++++- src/backend/replication/logical/conflict.c | 14 ++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index c599f6398a7..0d5e7098930 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -2502,9 +2502,25 @@ SELECT * FROM ... WHERE (replica_identity->>'r')::regclass[] = '{pg_class}'; -- SELECT * FROM pg_conflict.pg_conflict_log_16392 WHERE has_omitted_values; A value omitted this way cannot be recovered from the conflict log table. + + + Some limit is needed because replica_identity is a json value, which cannot exceed 1GB, and the text of a value - can be much longer than the value itself. + can be much longer than the value itself. However, the 64kB limit is + applied only after a key value's text representation has been produced, + so it limits how much text is kept in the conflict log table but does not + stop that text from exceeding 1GB in the first place. Normally this + cannot happen, because the publisher had to produce the same text, within + the same 1GB limit, in order to send the value. That does not hold when, + for example, the key column has a different data type on the subscriber, + the subscription uses + binary + mode so the value is not sent as text, or the subscriber's + or + setting differs from the + publisher's. In such cases the text can exceed 1GB, and the apply worker + fails with an error when it tries to log the conflict. diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c index ded115962e1..fc7d39d9591 100644 --- a/src/backend/replication/logical/conflict.c +++ b/src/backend/replication/logical/conflict.c @@ -1132,8 +1132,9 @@ build_index_key_json(EState *estate, Relation localrel, * unrelated to the size of the value we received, and a small key could * produce a json value over the 1GB limit and fail the apply worker. * Rendering each value with its type's output function keeps us to the - * representation the publisher sent, and matches what the server log does - * in BuildIndexValueDescription(). + * representation the publisher sent (when the column types and output + * settings match, see below), and matches what the server log does in + * BuildIndexValueDescription(). * * Only the key attributes are recorded. FormIndexDatum() above also fills * in any non-key (INCLUDE) columns, but those are not part of the replica @@ -1184,6 +1185,15 @@ build_index_key_json(EState *estate, Relation localrel, * instead index's tupleDesc. */ getTypeOutputInfo(indexDesc->rd_opcintype[i], &outfuncoid, &typisvarlena); + + /* + * XXX The cap below is checked only after the value is rendered. + * The publisher rendered the same value to send it, which bounds + * its length, unless the column types differ, binary = true, or + * the apply worker's bytea_output or quote_all_identifiers + * differs. Then the render can exceed 1GB and error out the apply + * worker. + */ outputstr = OidOutputFunctionCall(outfuncoid, values[i]); outputlen = strlen(outputstr); -- 2.49.0