Skip to content

Commit 69e8c7c

Browse files
committed
Limit memory usage of pg_walinspect functions.
GetWALRecordsInfo() and pg_get_wal_fpi_info() can leak memory across WAL record iterations. Fix this by using a temporary memory context that's reset for each WAL record iteraion. Also a use temporary context for loops in GetXLogSummaryStats(). The number of iterations is a small constant, so the previous behavior was not a leak, but fix for clarity (but no need to backport). Backport GetWALRecordsInfo() change to version 15. pg_get_wal_fpi_info() didn't exist in version 15. Reported-by: Peter Geoghegan Author: Bharath Rupireddy Discussion: https://www.postgresql.org/message-id/CAH2-WznLEJjn7ghmKOABOEZYuJvkTk%3DGKU3m0%2B-XBAH%2BerPiJQ%40mail.gmail.com Backpatch-through: 15
1 parent c6c3b3b commit 69e8c7c

File tree

1 file changed

+53
-5
lines changed

1 file changed

+53
-5
lines changed

contrib/pg_walinspect/pg_walinspect.c

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ pg_get_wal_fpi_info(PG_FUNCTION_ARGS)
304304
XLogRecPtr start_lsn;
305305
XLogRecPtr end_lsn;
306306
XLogReaderState *xlogreader;
307+
MemoryContext old_cxt;
308+
MemoryContext tmp_cxt;
307309

308310
start_lsn = PG_GETARG_LSN(0);
309311
end_lsn = PG_GETARG_LSN(1);
@@ -314,14 +316,26 @@ pg_get_wal_fpi_info(PG_FUNCTION_ARGS)
314316

315317
xlogreader = InitXLogReaderState(start_lsn);
316318

319+
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
320+
"pg_get_wal_fpi_info temporary cxt",
321+
ALLOCSET_DEFAULT_SIZES);
322+
317323
while (ReadNextXLogRecord(xlogreader) &&
318324
xlogreader->EndRecPtr <= end_lsn)
319325
{
326+
/* Use the tmp context so we can clean up after each tuple is done */
327+
old_cxt = MemoryContextSwitchTo(tmp_cxt);
328+
320329
GetWALFPIInfo(fcinfo, xlogreader);
321330

331+
/* clean up and switch back */
332+
MemoryContextSwitchTo(old_cxt);
333+
MemoryContextReset(tmp_cxt);
334+
322335
CHECK_FOR_INTERRUPTS();
323336
}
324337

338+
MemoryContextDelete(tmp_cxt);
325339
pfree(xlogreader->private_data);
326340
XLogReaderFree(xlogreader);
327341

@@ -440,23 +454,37 @@ GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
440454
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
441455
Datum values[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
442456
bool nulls[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
457+
MemoryContext old_cxt;
458+
MemoryContext tmp_cxt;
443459

444460
InitMaterializedSRF(fcinfo, 0);
445461

446462
xlogreader = InitXLogReaderState(start_lsn);
447463

464+
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
465+
"GetWALRecordsInfo temporary cxt",
466+
ALLOCSET_DEFAULT_SIZES);
467+
448468
while (ReadNextXLogRecord(xlogreader) &&
449469
xlogreader->EndRecPtr <= end_lsn)
450470
{
471+
/* Use the tmp context so we can clean up after each tuple is done */
472+
old_cxt = MemoryContextSwitchTo(tmp_cxt);
473+
451474
GetWALRecordInfo(xlogreader, values, nulls,
452475
PG_GET_WAL_RECORDS_INFO_COLS);
453476

454477
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
455478
values, nulls);
456479

480+
/* clean up and switch back */
481+
MemoryContextSwitchTo(old_cxt);
482+
MemoryContextReset(tmp_cxt);
483+
457484
CHECK_FOR_INTERRUPTS();
458485
}
459486

487+
MemoryContextDelete(tmp_cxt);
460488
pfree(xlogreader->private_data);
461489
XLogReaderFree(xlogreader);
462490

@@ -560,11 +588,13 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
560588
Datum *values, bool *nulls, uint32 ncols,
561589
bool stats_per_record)
562590
{
563-
uint64 total_count = 0;
564-
uint64 total_rec_len = 0;
565-
uint64 total_fpi_len = 0;
566-
uint64 total_len = 0;
567-
int ri;
591+
MemoryContext old_cxt;
592+
MemoryContext tmp_cxt;
593+
uint64 total_count = 0;
594+
uint64 total_rec_len = 0;
595+
uint64 total_fpi_len = 0;
596+
uint64 total_len = 0;
597+
int ri;
568598

569599
/*
570600
* Each row shows its percentages of the total, so make a first pass to
@@ -581,6 +611,10 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
581611
}
582612
total_len = total_rec_len + total_fpi_len;
583613

614+
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
615+
"GetXLogSummaryStats temporary cxt",
616+
ALLOCSET_DEFAULT_SIZES);
617+
584618
for (ri = 0; ri <= RM_MAX_ID; ri++)
585619
{
586620
uint64 count;
@@ -614,6 +648,8 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
614648
if (count == 0)
615649
continue;
616650

651+
old_cxt = MemoryContextSwitchTo(tmp_cxt);
652+
617653
/* the upper four bits in xl_info are the rmgr's */
618654
id = desc.rm_identify(rj << 4);
619655
if (id == NULL)
@@ -626,6 +662,10 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
626662

627663
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
628664
values, nulls);
665+
666+
/* clean up and switch back */
667+
MemoryContextSwitchTo(old_cxt);
668+
MemoryContextReset(tmp_cxt);
629669
}
630670
}
631671
else
@@ -635,14 +675,22 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
635675
fpi_len = stats->rmgr_stats[ri].fpi_len;
636676
tot_len = rec_len + fpi_len;
637677

678+
old_cxt = MemoryContextSwitchTo(tmp_cxt);
679+
638680
FillXLogStatsRow(desc.rm_name, count, total_count, rec_len,
639681
total_rec_len, fpi_len, total_fpi_len, tot_len,
640682
total_len, values, nulls, ncols);
641683

642684
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
643685
values, nulls);
686+
687+
/* clean up and switch back */
688+
MemoryContextSwitchTo(old_cxt);
689+
MemoryContextReset(tmp_cxt);
644690
}
645691
}
692+
693+
MemoryContextDelete(tmp_cxt);
646694
}
647695

648696
/*

0 commit comments

Comments
 (0)