| From: | vignesh C <vignesh21(at)gmail(dot)com> |
|---|---|
| To: | Dilip Kumar <dilipbalaut(at)gmail(dot)com> |
| Cc: | shveta malik <shveta(dot)malik(at)gmail(dot)com>, Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>, "Zhijie Hou (Fujitsu)" <houzj(dot)fnst(at)fujitsu(dot)com>, Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>, saurabh singh <saurabh(dot)singh214(at)gmail(dot)com>, Robert Haas <robertmhaas(at)gmail(dot)com>, Peter Smith <smithpb2250(at)gmail(dot)com>, Nisha Moond <nisha(dot)moond412(at)gmail(dot)com>, Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Subject: | Re: Proposal: Conflict log history table for Logical Replication |
| Date: | 2026-08-31 12:53:41 |
| Message-ID: | CALDaNm2H9DS3C9DOu6ZqPQ56yJYRz8q1xU9mz__MCc2FT_c2rQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Sun, 30 Aug 2026 at 20:43, Dilip Kumar <dilipbalaut(at)gmail(dot)com> wrote:
>
> On Wed, Aug 26, 2026 at 8:01 PM Dilip Kumar <dilipbalaut(at)gmail(dot)com> wrote:
> >
> Updated version of 0002, based on offlist testing by Nisha, revealed
> that the assumption that a column's maximum size could become 6x its
> original size during JSON conversion is incorrect. One edge case is a
> column of type array (int[]). A huge array but mostly empty(all NULL),
> can have a small storage size but produce a much larger JSON since
> each element is serialized. For example
> - A 4096 byte text column becomes ~24KB json (6x), which stays under
> the budget even accumulated across all columns (24,554 × 1600 × 3 ≈
> 118 MB).
> - But a 4096 byte all-NULL int[] column can produce ~480KB(120x) of
> json in a edge case.
> Based on this, Amit suggested a offlist POC of the patch which Nisha
> and I further modified. It still needs more review, testing, and logic
> validation, but I am sharing it here so we can review and provide
> feedback.
Thanks for the updated patch.
Few comments:
1) The omitted length shown in some cases is not correct.
This can be reproduced by the following:
-- Create type and table in both publisher and subscriber
CREATE TYPE bigenum AS ENUM ('x');
CREATE TABLE conf_marker (a int PRIMARY KEY, arr int[], e bigenum);
-- Subscriber
-- Only the subscriber serializes to JSON, so only it needs the cast. This is
-- what puts the enum column on datum_to_json_internal()'s JSONTYPE_CAST path
-- with a rendering far larger than the value.
CREATE FUNCTION bigenum_to_json(bigenum) RETURNS json
AS $$ SELECT to_json(repeat('a', 30000)) $$
LANGUAGE sql IMMUTABLE;
CREATE CAST (bigenum AS json) WITH FUNCTION bigenum_to_json(bigenum);
-- Have logical replication setup with above using
track_commit_timestamp as true in subscriber.
-- Publisher
INSERT INTO conf_marker VALUES (1, NULL, NULL);
-- Subscriber
-- Delete the record in subscriber to simulate update_missing conflict
DELETE FROM conf_marker;
-- Publisher
UPDATE conf_marker
SET arr = array_fill(NULL::int, ARRAY[4000]), e = 'x'
WHERE a = 1;
-- The above update generates update_missing conflict in the subscriber
postgres=# select relname, conflict_type, remote_tuple,
has_omitted_values from pg_conflict.pg_conflict_log_16395;
relname | conflict_type |
remote_tuple | has_omitted_values
-------------+----------------+-----------------------------------------------------------------------------+--------------------
conf_marker | update_missing |
{"a":1,"arr":{"omitted":true,"length":524},"e":{"omitted":true,"length":0}}
| t
(1 row)
The omitted length shown as 0 for column e is not correct.
Also the 524 shown for column arr seems incorrect, should it be 20001:
SELECT length(to_json(array_fill(NULL::int, ARRAY[4000]))::text);
length
--------
20001
(1 row)
2) The 16kB mentioned is not correct:
+ <para>
+ A column value is either recorded exactly or not at all; it is never
+ truncated. Any value larger than 16kB is replaced in the
+ JSON columns by an object recording that it was omitted
+ together with its length in bytes, for example:
+<programlisting>
+{"a" : 1, "b" : {"omitted":true,"length":190000000}}
We can see omitted column having lengths 3004, 5004, 10004, 15004
which are less than 16kB:
postgres=# select relname, conflict_type, remote_tuple from
pg_conflict.pg_conflict_log_16395 where has_omitted_values = true;
relname | conflict_type | remote_tuple
---------+-----------------------+--------------------------------------------------
tab | update_origin_differs |
{"a":10,"b":{"omitted":true,"length":190000004}}
tab | update_origin_differs | {"a":10,"b":{"omitted":true,"length":15004}}
tab | update_origin_differs | {"a":10,"b":{"omitted":true,"length":10004}}
tab | update_origin_differs | {"a":10,"b":{"omitted":true,"length":5004}}
tab | update_origin_differs | {"a":10,"b":{"omitted":true,"length":3004}}
(5 rows)
This needs to be updated accordingly to mention based on the new code
changes done.
3) The actual output from clt does not have the spaces around "a" and "b":
<programlisting>
{"a" : 1, "b" : {"omitted":true,"length":190000000}}
</programlisting>
I felt it should be:
{"a":1,"b":{"omitted":true,"length":190000000}}
Regards,
Vignesh
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Zsolt Parragi | 2026-08-31 13:10:15 | Re: Offline data checksum changes can cause incorrect checksum state on standbys |
| Previous Message | Matthias van de Meent | 2026-08-31 12:39:30 | Re: Bug: Whole-row var in indexes corrupts indexes after DDL |