Hi, hackers! Existing code detoasts every external attribute into a Datum array, then calls heap_fill_tuple on the complete set. So around 2x tuple length memory is allocated (result tuple plus all detoasted values). If I remember correctly, toasted tuples can be up to 1GB long, so even before the length limit, tuple flattening can strain the local backend's memory beyond the need.
Mainly this affects logical replication with REPLICA IDENTITY FULL with tables with several large toasted columns: every UPDATE/DELETE flattens the old tuple. The other callers -- catalog cache loading and PL/pgSQL record assignment -- handle smaller tuples where the difference is negligible. In the patch attached I flatten TOASTed tuples with a two-stage approach: - For each external attribute build a VARHDRSZ stub whose header matches what detoast_external_attr will return (size and compression tag). Pass the stubs through heap_fill_tuple_attr with skip_copy=true so alignment and sizes are computed without copying data. attr_data[] records the destination pointer for every non-null attribute. - Detoast one attribute at a time into the pre-computed position, pfree immediately. Peak memory usage decreased from 2x tuple to 1x tuple plus one detoasted value. The extra per-attribute cost (one VARHDRSZ palloc/pfree for the stub, toast pointer decode for compression detection) is negligible compared the toast-fetch I/O that dominates every call. Please look at the patch attached, reviews are always welcome! Regards, Pavel Borisov Supabase
v1-0001-Halve-peak-memory-allocation-for-toast_flatten_tu.patch
Description: Binary data
