@@ -136,6 +136,14 @@ static char primary_last_archived[MAX_XFN_CHARS + 1];
136136static TimeLineID primary_last_archived_tli = 0 ;
137137static 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 */
140148static void WalRcvFetchTimeLineHistoryFiles (TimeLineID first , TimeLineID last );
141149static 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