Skip to content

Commit e9b9346

Browse files
authored
[LKB-5974][PG_17] Add the ability to reduce FPI (#792)
# Context **Why is a full page image(FPI) unnecessary in Lakebase/Neon?** In vanilla PostgreSQL, full-page images (FPIs) are required to guard against torn page writes caused by non-atomic 8 KB page writes to local storage. However, in the Lakehouse/Neon architecture, FPIs are generally not needed because the compute node never overwrites pages in place. Instead, all modifications are logged as WAL records and sent to the pageserver, which appends them into the storage layer’s log-structured layout. Since there is no local data-file page overwriting, the torn-page risk that FPIs protect against does not exist on the compute node’s local storage. **Why do we want to reduce the full page image?** Full-page images amplify every small update into an 8 KB WAL write, which drives up WAL volume and causes Reverse ETL ingestion to hit the max_wal_rate limit in Serverless Lakebase. **Why not eliminate full-page images entirely?** Full-page images help the pageserver reduce read-path I/O by providing complete page snapshots that can be used during page reconstruction. Removing FPIs entirely could negatively impact read performance. # Summary This PR(along with the hadron PR: https://github.com/databricks-eng/hadron/pull/3155 )implements reducing FPI to improve write performance for data ingestion workloads, specifically reverse ETL and Spark streaming use cases. The feature is opt-in per table to avoid unintended impact on other customers. ## Performance Impact Testing shows 3.75x write throughput improvement without significantly impacting read performance. See [performance analysis. ](https://docs.google.com/document/d/19yY8nogB5hTjXmAsX-5GT4LVDNX3c86_gK2Y6Fu7TTw/edit?tab=t.quizviqwhqh0#heading=h.kw4pwpzifs9u) # Major changes ## Probabilistic reducing FPI generation to 5% Added `neon_should_suppress_fpi` which will randomly determine if we should suppress FPI with a default value of 5%. The 5% is chosen to: - Significantly reducing WAL volume to avoid write-amplification bottlenecks. - Retaining a small number of FPIs so the pageserver still benefits from occasional full-page snapshots, helping maintain efficient read-path performance.
1 parent 178558d commit e9b9346

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

src/backend/access/transam/xlog.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,10 @@ XLogInsertRecord(XLogRecData *rdata,
867867
* our local copy but not force a recomputation. (If doPageWrites was
868868
* just turned off, we could recompute the record without full pages,
869869
* but we choose not to bother.)
870+
*
871+
* However, if suppress_fpi is true, we skip the recomputation check
872+
* since we're deliberately suppressing full page images for this
873+
* record via the FPI control hook.
870874
*/
871875
if (RedoRecPtr != Insert->RedoRecPtr)
872876
{
@@ -877,7 +881,8 @@ XLogInsertRecord(XLogRecData *rdata,
877881

878882
if (doPageWrites &&
879883
(!prevDoPageWrites ||
880-
(fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr)))
884+
(!suppress_fpi &&
885+
fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr)))
881886
{
882887
/*
883888
* Oops, some buffer now needs to be backed up that the caller

src/backend/access/transam/xloginsert.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ int max_replication_apply_lag;
9393
int max_replication_flush_lag;
9494
int max_replication_write_lag;
9595

96+
/* NEON: Hook to determine if FPI should be suppressed for a WAL record */
97+
xlog_should_suppress_fpi_hook_type xlog_should_suppress_fpi_hook = NULL;
98+
99+
/* NEON: Global flag to suppress FPI for current WAL record */
100+
bool suppress_fpi = false;
101+
96102
static registered_buffer *registered_buffers;
97103
static int max_registered_buffers; /* allocated size */
98104
static int max_registered_block_id = 0; /* highest block_id + 1 currently
@@ -529,6 +535,16 @@ XLogInsert(RmgrId rmid, uint8 info)
529535
*/
530536
GetFullPageWriteInfo(&RedoRecPtr, &doPageWrites);
531537

538+
/*
539+
* NEON: Check if we should suppress FPI for this WAL record.
540+
*/
541+
suppress_fpi = false;
542+
if (xlog_should_suppress_fpi_hook != NULL) {
543+
suppress_fpi = xlog_should_suppress_fpi_hook();
544+
elog(DEBUG1, "FPI suppress hook called: suppress_fpi=%d, doPageWrites=%d",
545+
suppress_fpi, doPageWrites);
546+
}
547+
532548
rdt = XLogRecordAssemble(rmid, info, RedoRecPtr, doPageWrites,
533549
&fpw_lsn, &num_fpi, &topxid_included);
534550

@@ -618,7 +634,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
618634
needs_backup = true;
619635
else if (regbuf->flags & REGBUF_NO_IMAGE)
620636
needs_backup = false;
621-
else if (!doPageWrites)
637+
else if (!doPageWrites || suppress_fpi)
622638
needs_backup = false;
623639
else
624640
{
@@ -630,8 +646,13 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
630646
XLogRecPtr page_lsn = PageGetLSN(regbuf->page);
631647

632648
needs_backup = (page_lsn <= RedoRecPtr);
649+
633650
if (!needs_backup)
634651
{
652+
/*
653+
* Set fpw_lsn to signal that this record should be
654+
* recomputed if doPageWrites changes.
655+
*/
635656
if (*fpw_lsn == InvalidXLogRecPtr || page_lsn < *fpw_lsn)
636657
*fpw_lsn = page_lsn;
637658
}

src/include/access/xloginsert.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ extern int max_replication_apply_lag;
4343
extern int max_replication_flush_lag;
4444
extern int max_replication_write_lag;
4545

46+
/* NEON: Hook to determine if FPI should be suppressed for a WAL record
47+
* Returns true to suppress FPI, false to allow FPI
48+
* Applies to all resource managers for checkpoint-based FPI triggers.
49+
*/
50+
typedef bool (*xlog_should_suppress_fpi_hook_type)(void);
51+
extern PGDLLIMPORT xlog_should_suppress_fpi_hook_type xlog_should_suppress_fpi_hook;
52+
53+
/* NEON: Flag set per-record to suppress FPI (set by xlog_should_suppress_fpi_hook) */
54+
extern bool suppress_fpi;
55+
4656
/* prototypes for public functions in xloginsert.c: */
4757
extern void XLogBeginInsert(void);
4858
extern void XLogSetRecordFlags(uint8 flags);

0 commit comments

Comments
 (0)