Skip to content

Commit 52e387d

Browse files
x4mrobot-cloud-aw
authored andcommitted
Optimize ProcessArchivalReport to avoid directory scans
When archive status reports arrive sequentially on the same timeline, directly generate expected WAL filenames and mark them as archived instead of scanning the entire archive_status directory. This optimization reduces overhead in the common case where the primary continuously archives segments. Directory scan is still used when: - Timeline changes (to handle ancestor timelines) - First report received - Non-sequential reports XLogArchiveForceDone() handles all cases internally (checking if .done exists, if .ready exists, or creating .done if neither exists), so no pre-check is needed.
1 parent cccec5e commit 52e387d

1 file changed

Lines changed: 132 additions & 64 deletions

File tree

src/backend/replication/walreceiver.c

Lines changed: 132 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ static char primary_last_archived[MAX_XFN_CHARS + 1];
136136
static TimeLineID primary_last_archived_tli = 0;
137137
static XLogSegNo primary_last_archived_segno = 0;
138138

139+
/*
140+
* Last segment we successfully marked as .done. Used to optimize
141+
* ProcessArchivalReport() by generating expected filenames instead
142+
* of scanning the archive_status directory.
143+
*/
144+
static TimeLineID last_processed_tli = 0;
145+
static XLogSegNo last_processed_segno = 0;
146+
139147
/* Prototypes for private functions */
140148
static void WalRcvFetchTimeLineHistoryFiles(TimeLineID first, TimeLineID last);
141149
static void WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI);
@@ -1321,10 +1329,9 @@ ProcessArchivalReport(void)
13211329
{
13221330
TimeLineID reported_tli;
13231331
XLogSegNo reported_segno;
1324-
DIR *status_dir;
1325-
struct dirent *status_de;
13261332
char status_path[MAXPGPATH];
1327-
List *tli_history = NIL;
1333+
bool use_direct_check = false;
1334+
XLogSegNo start_segno;
13281335

13291336
elog(DEBUG2, "received archival report from primary: %s",
13301337
primary_last_archived);
@@ -1344,90 +1351,151 @@ ProcessArchivalReport(void)
13441351
primary_last_archived_tli = reported_tli;
13451352
primary_last_archived_segno = reported_segno;
13461353

1347-
/* Scan archive_status directory for .ready files */
1348-
snprintf(status_path, MAXPGPATH, XLOGDIR "/archive_status");
1349-
status_dir = AllocateDir(status_path);
1350-
if (status_dir == NULL)
1354+
/*
1355+
* Optimization: If the new report is on the same timeline as the last
1356+
* processed segment and moves forward, we can directly check for .ready
1357+
* files for segments between last_processed_segno and reported_segno
1358+
* instead of scanning the entire archive_status directory.
1359+
*
1360+
* Fall back to directory scan if:
1361+
* - Timeline changed (need to handle ancestor timelines)
1362+
* - This is the first report (last_processed_tli == 0)
1363+
* - Reported segment is not ahead (nothing new to process)
1364+
*/
1365+
if (last_processed_tli == reported_tli &&
1366+
last_processed_tli != 0 &&
1367+
reported_segno > last_processed_segno)
13511368
{
1352-
elog(DEBUG2, "could not open archive_status directory: %m");
1353-
return;
1369+
use_direct_check = true;
1370+
start_segno = last_processed_segno + 1;
13541371
}
13551372

1356-
while ((status_de = ReadDir(status_dir, status_path)) != NULL)
1373+
if (use_direct_check)
13571374
{
1358-
char *ready_suffix;
1359-
char walfile[MAXPGPATH];
1360-
TimeLineID file_tli;
1361-
XLogSegNo file_segno;
1362-
/* Look for .ready files only */
1363-
ready_suffix = strstr(status_de->d_name, ".ready");
1364-
if (ready_suffix == NULL || ready_suffix[6] != '\0')
1365-
continue;
1366-
1367-
/* Extract WAL filename (remove .ready suffix) */
1368-
strlcpy(walfile, status_de->d_name, ready_suffix - status_de->d_name + 1);
1369-
1370-
/* Parse the WAL filename */
1371-
if (!IsXLogFileName(walfile))
1372-
continue;
1373-
1374-
XLogFromFileName(walfile, &file_tli, &file_segno, wal_segment_size);
1375-
13761375
/*
1377-
* Mark as .done if:
1378-
* 1. Same timeline and segment <= reported segment, OR
1379-
* 2. Ancestor timeline and segment is before the timeline switch point
1380-
*
1381-
* For ancestor timelines: if primary archived segment X on timeline T,
1382-
* then all segments on ancestor timelines before the switch to T must
1383-
* have been archived (they're required to reach timeline T).
1376+
* Direct check: generate filenames for expected segments.
1377+
* XLogArchiveForceDone() will handle the case where .ready doesn't
1378+
* exist or .done already exists, so no need to stat() first.
13841379
*/
1385-
if (file_tli == reported_tli && file_segno <= reported_segno)
1380+
XLogSegNo segno;
1381+
1382+
for (segno = start_segno; segno <= reported_segno; segno++)
13861383
{
1387-
/* Same timeline, segment already archived */
1384+
char walfile[MAXFNAMELEN];
1385+
1386+
/* Generate WAL filename and mark as archived */
1387+
XLogFileName(walfile, reported_tli, segno, wal_segment_size);
13881388
XLogArchiveForceDone(walfile);
13891389
elog(DEBUG3, "marked WAL segment %s as archived (primary archived up to %s)",
13901390
walfile, primary_last_archived);
1391+
1392+
/* Track the last segment we processed */
1393+
last_processed_tli = reported_tli;
1394+
last_processed_segno = segno;
1395+
}
1396+
}
1397+
else
1398+
{
1399+
/*
1400+
* Directory scan: needed when timeline changed or first report.
1401+
* This handles both same-timeline and ancestor-timeline cases.
1402+
*/
1403+
DIR *status_dir;
1404+
struct dirent *status_de;
1405+
List *tli_history = NIL;
1406+
1407+
snprintf(status_path, MAXPGPATH, XLOGDIR "/archive_status");
1408+
status_dir = AllocateDir(status_path);
1409+
if (status_dir == NULL)
1410+
{
1411+
elog(DEBUG2, "could not open archive_status directory: %m");
1412+
return;
13911413
}
1392-
else if (file_tli != reported_tli)
1414+
1415+
while ((status_de = ReadDir(status_dir, status_path)) != NULL)
13931416
{
1417+
char *ready_suffix;
1418+
char walfile[MAXPGPATH];
1419+
TimeLineID file_tli;
1420+
XLogSegNo file_segno;
1421+
1422+
/* Look for .ready files only */
1423+
ready_suffix = strstr(status_de->d_name, ".ready");
1424+
if (ready_suffix == NULL || ready_suffix[6] != '\0')
1425+
continue;
1426+
1427+
/* Extract WAL filename (remove .ready suffix) */
1428+
strlcpy(walfile, status_de->d_name, ready_suffix - status_de->d_name + 1);
1429+
1430+
/* Parse the WAL filename */
1431+
if (!IsXLogFileName(walfile))
1432+
continue;
1433+
1434+
XLogFromFileName(walfile, &file_tli, &file_segno, wal_segment_size);
1435+
13941436
/*
1395-
* Different timeline - check if it's an ancestor and if this
1396-
* segment is before the timeline switch point. Only read timeline
1397-
* history if we haven't already (lazy loading).
1437+
* Mark as .done if:
1438+
* 1. Same timeline and segment <= reported segment, OR
1439+
* 2. Ancestor timeline and segment is before the timeline switch point
13981440
*
1399-
* Note: Timelines form a tree structure, not a linear sequence,
1400-
* so we can't use < or > to compare them.
1441+
* For ancestor timelines: if primary archived segment X on timeline T,
1442+
* then all segments on ancestor timelines before the switch to T must
1443+
* have been archived (they're required to reach timeline T).
14011444
*/
1402-
if (tli_history == NIL)
1403-
tli_history = readTimeLineHistory(reported_tli);
1404-
1405-
if (tliInHistory(file_tli, tli_history))
1445+
if (file_tli == reported_tli && file_segno <= reported_segno)
1446+
{
1447+
/* Same timeline, segment already archived */
1448+
XLogArchiveForceDone(walfile);
1449+
elog(DEBUG3, "marked WAL segment %s as archived (primary archived up to %s)",
1450+
walfile, primary_last_archived);
1451+
}
1452+
else if (file_tli != reported_tli)
14061453
{
1407-
XLogRecPtr switchpoint;
1408-
XLogSegNo switchpoint_segno;
1409-
1410-
/* Get the point where we switched away from this timeline */
1411-
switchpoint = tliSwitchPoint(file_tli, tli_history, NULL);
1412-
14131454
/*
1414-
* If the segment is at or before the switch point, it must have
1415-
* been archived (it's required to reach the reported timeline).
1416-
* The segment containing the switch point belongs to the old
1417-
* timeline up to the switch point and should be archived.
1455+
* Different timeline - check if it's an ancestor and if this
1456+
* segment is before the timeline switch point. Only read timeline
1457+
* history if we haven't already (lazy loading).
1458+
*
1459+
* Note: Timelines form a tree structure, not a linear sequence,
1460+
* so we can't use < or > to compare them.
14181461
*/
1419-
XLByteToSeg(switchpoint, switchpoint_segno, wal_segment_size);
1420-
if (file_segno <= switchpoint_segno)
1462+
if (tli_history == NIL)
1463+
tli_history = readTimeLineHistory(reported_tli);
1464+
1465+
if (tliInHistory(file_tli, tli_history))
14211466
{
1422-
XLogArchiveForceDone(walfile);
1423-
elog(DEBUG3, "marked ancestor timeline segment %s as archived (before switch to timeline %u)",
1424-
walfile, reported_tli);
1467+
XLogRecPtr switchpoint;
1468+
XLogSegNo switchpoint_segno;
1469+
1470+
/* Get the point where we switched away from this timeline */
1471+
switchpoint = tliSwitchPoint(file_tli, tli_history, NULL);
1472+
1473+
/*
1474+
* If the segment is at or before the switch point, it must have
1475+
* been archived (it's required to reach the reported timeline).
1476+
* The segment containing the switch point belongs to the old
1477+
* timeline up to the switch point and should be archived.
1478+
*/
1479+
XLByteToSeg(switchpoint, switchpoint_segno, wal_segment_size);
1480+
if (file_segno <= switchpoint_segno)
1481+
{
1482+
XLogArchiveForceDone(walfile);
1483+
elog(DEBUG3, "marked ancestor timeline segment %s as archived (before switch to timeline %u)",
1484+
walfile, reported_tli);
1485+
}
14251486
}
14261487
}
14271488
}
1428-
}
14291489

1430-
FreeDir(status_dir);
1490+
FreeDir(status_dir);
1491+
1492+
/*
1493+
* After a full directory scan following a timeline change, update
1494+
* our tracking to the newly reported position for future optimizations.
1495+
*/
1496+
last_processed_tli = reported_tli;
1497+
last_processed_segno = reported_segno;
1498+
}
14311499
}
14321500

14331501
/*

0 commit comments

Comments
 (0)