@@ -196,6 +196,9 @@ static Size ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn
196196static void ReorderBufferRestoreChange (ReorderBuffer * rb , ReorderBufferTXN * txn ,
197197 char * change );
198198static void ReorderBufferRestoreCleanup (ReorderBuffer * rb , ReorderBufferTXN * txn );
199+ static void ReorderBufferCleanupSerializedTXNs (const char * slotname );
200+ static void ReorderBufferSerializedPath (char * path , ReplicationSlot * slot ,
201+ TransactionId xid , XLogSegNo segno );
199202
200203static void ReorderBufferFreeSnap (ReorderBuffer * rb , Snapshot snap );
201204static Snapshot ReorderBufferCopySnap (ReorderBuffer * rb , Snapshot orig_snap ,
@@ -214,7 +217,8 @@ static void ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *t
214217
215218
216219/*
217- * Allocate a new ReorderBuffer
220+ * Allocate a new ReorderBuffer and clean out any old serialized state from
221+ * prior ReorderBuffer instances for the same slot.
218222 */
219223ReorderBuffer *
220224ReorderBufferAllocate (void )
@@ -223,6 +227,8 @@ ReorderBufferAllocate(void)
223227 HASHCTL hash_ctl ;
224228 MemoryContext new_ctx ;
225229
230+ Assert (MyReplicationSlot != NULL );
231+
226232 /* allocate memory in own context, to have better accountability */
227233 new_ctx = AllocSetContextCreate (CurrentMemoryContext ,
228234 "ReorderBuffer" ,
@@ -269,6 +275,13 @@ ReorderBufferAllocate(void)
269275
270276 dlist_init (& buffer -> toplevel_by_lsn );
271277
278+ /*
279+ * Ensure there's no stale data from prior uses of this slot, in case some
280+ * prior exit avoided calling ReorderBufferFree. Failure to do this can
281+ * produce duplicated txns, and it's very cheap if there's nothing there.
282+ */
283+ ReorderBufferCleanupSerializedTXNs (NameStr (MyReplicationSlot -> data .name ));
284+
272285 return buffer ;
273286}
274287
@@ -285,6 +298,9 @@ ReorderBufferFree(ReorderBuffer *rb)
285298 * memory context.
286299 */
287300 MemoryContextDelete (context );
301+
302+ /* Free disk space used by unconsumed reorder buffers */
303+ ReorderBufferCleanupSerializedTXNs (NameStr (MyReplicationSlot -> data .name ));
288304}
289305
290306/*
@@ -2030,7 +2046,6 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
20302046 int fd = -1 ;
20312047 XLogSegNo curOpenSegNo = 0 ;
20322048 Size spilled = 0 ;
2033- char path [MAXPGPATH ];
20342049
20352050 elog (DEBUG2 , "spill %u changes in XID %u to disk" ,
20362051 (uint32 ) txn -> nentries_mem , txn -> xid );
@@ -2058,21 +2073,19 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
20582073 if (fd == -1 ||
20592074 !XLByteInSeg (change -> lsn , curOpenSegNo , wal_segment_size ))
20602075 {
2061- XLogRecPtr recptr ;
2076+ char path [ MAXPGPATH ] ;
20622077
20632078 if (fd != -1 )
20642079 CloseTransientFile (fd );
20652080
20662081 XLByteToSeg (change -> lsn , curOpenSegNo , wal_segment_size );
2067- XLogSegNoOffsetToRecPtr (curOpenSegNo , 0 , recptr , wal_segment_size );
20682082
20692083 /*
20702084 * No need to care about TLIs here, only used during a single run,
20712085 * so each LSN only maps to a specific WAL record.
20722086 */
2073- sprintf (path , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2074- NameStr (MyReplicationSlot -> data .name ), txn -> xid ,
2075- (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2087+ ReorderBufferSerializedPath (path , MyReplicationSlot , txn -> xid ,
2088+ curOpenSegNo );
20762089
20772090 /* open segment, create it if necessary */
20782091 fd = OpenTransientFile (path ,
@@ -2081,8 +2094,7 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
20812094 if (fd < 0 )
20822095 ereport (ERROR ,
20832096 (errcode_for_file_access (),
2084- errmsg ("could not open file \"%s\": %m" ,
2085- path )));
2097+ errmsg ("could not open file \"%s\": %m" , path )));
20862098 }
20872099
20882100 ReorderBufferSerializeChange (rb , txn , fd , change );
@@ -2300,25 +2312,20 @@ ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn,
23002312
23012313 if (* fd == -1 )
23022314 {
2303- XLogRecPtr recptr ;
23042315 char path [MAXPGPATH ];
23052316
23062317 /* first time in */
23072318 if (* segno == 0 )
2308- {
23092319 XLByteToSeg (txn -> first_lsn , * segno , wal_segment_size );
2310- }
23112320
23122321 Assert (* segno != 0 || dlist_is_empty (& txn -> changes ));
2313- XLogSegNoOffsetToRecPtr (* segno , 0 , recptr , wal_segment_size );
23142322
23152323 /*
23162324 * No need to care about TLIs here, only used during a single run,
23172325 * so each LSN only maps to a specific WAL record.
23182326 */
2319- sprintf (path , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2320- NameStr (MyReplicationSlot -> data .name ), txn -> xid ,
2321- (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2327+ ReorderBufferSerializedPath (path , MyReplicationSlot , txn -> xid ,
2328+ * segno );
23222329
23232330 * fd = OpenTransientFile (path , O_RDONLY | PG_BINARY );
23242331 if (* fd < 0 && errno == ENOENT )
@@ -2332,7 +2339,6 @@ ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn,
23322339 (errcode_for_file_access (),
23332340 errmsg ("could not open file \"%s\": %m" ,
23342341 path )));
2335-
23362342 }
23372343
23382344 /*
@@ -2554,20 +2560,72 @@ ReorderBufferRestoreCleanup(ReorderBuffer *rb, ReorderBufferTXN *txn)
25542560 for (cur = first ; cur <= last ; cur ++ )
25552561 {
25562562 char path [MAXPGPATH ];
2557- XLogRecPtr recptr ;
2558-
2559- XLogSegNoOffsetToRecPtr (cur , 0 , recptr , wal_segment_size );
25602563
2561- sprintf (path , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2562- NameStr (MyReplicationSlot -> data .name ), txn -> xid ,
2563- (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2564+ ReorderBufferSerializedPath (path , MyReplicationSlot , txn -> xid , cur );
25642565 if (unlink (path ) != 0 && errno != ENOENT )
25652566 ereport (ERROR ,
25662567 (errcode_for_file_access (),
25672568 errmsg ("could not remove file \"%s\": %m" , path )));
25682569 }
25692570}
25702571
2572+ /*
2573+ * Remove any leftover serialized reorder buffers from a slot directory after a
2574+ * prior crash or decoding session exit.
2575+ */
2576+ static void
2577+ ReorderBufferCleanupSerializedTXNs (const char * slotname )
2578+ {
2579+ DIR * spill_dir ;
2580+ struct dirent * spill_de ;
2581+ struct stat statbuf ;
2582+ char path [MAXPGPATH * 2 + 12 ];
2583+
2584+ sprintf (path , "pg_replslot/%s" , slotname );
2585+
2586+ /* we're only handling directories here, skip if it's not ours */
2587+ if (lstat (path , & statbuf ) == 0 && !S_ISDIR (statbuf .st_mode ))
2588+ return ;
2589+
2590+ spill_dir = AllocateDir (path );
2591+ while ((spill_de = ReadDirExtended (spill_dir , path , INFO )) != NULL )
2592+ {
2593+ /* only look at names that can be ours */
2594+ if (strncmp (spill_de -> d_name , "xid" , 3 ) == 0 )
2595+ {
2596+ snprintf (path , sizeof (path ),
2597+ "pg_replslot/%s/%s" , slotname ,
2598+ spill_de -> d_name );
2599+
2600+ if (unlink (path ) != 0 )
2601+ ereport (ERROR ,
2602+ (errcode_for_file_access (),
2603+ errmsg ("could not remove file \"%s\" during removal of pg_replslot/%s/*.xid: %m" ,
2604+ path , slotname )));
2605+ }
2606+ }
2607+ FreeDir (spill_dir );
2608+ }
2609+
2610+ /*
2611+ * Given a replication slot, transaction ID and segment number, fill in the
2612+ * corresponding spill file into 'path', which is a caller-owned buffer of size
2613+ * at least MAXPGPATH.
2614+ */
2615+ static void
2616+ ReorderBufferSerializedPath (char * path , ReplicationSlot * slot , TransactionId xid ,
2617+ XLogSegNo segno )
2618+ {
2619+ XLogRecPtr recptr ;
2620+
2621+ XLogSegNoOffsetToRecPtr (segno , 0 , recptr , wal_segment_size );
2622+
2623+ snprintf (path , MAXPGPATH , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2624+ NameStr (MyReplicationSlot -> data .name ),
2625+ xid ,
2626+ (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2627+ }
2628+
25712629/*
25722630 * Delete all data spilled to disk after we've restarted/crashed. It will be
25732631 * recreated when the respective slots are reused.
@@ -2578,15 +2636,9 @@ StartupReorderBuffer(void)
25782636 DIR * logical_dir ;
25792637 struct dirent * logical_de ;
25802638
2581- DIR * spill_dir ;
2582- struct dirent * spill_de ;
2583-
25842639 logical_dir = AllocateDir ("pg_replslot" );
25852640 while ((logical_de = ReadDir (logical_dir , "pg_replslot" )) != NULL )
25862641 {
2587- struct stat statbuf ;
2588- char path [MAXPGPATH * 2 + 12 ];
2589-
25902642 if (strcmp (logical_de -> d_name , "." ) == 0 ||
25912643 strcmp (logical_de -> d_name , ".." ) == 0 )
25922644 continue ;
@@ -2599,33 +2651,7 @@ StartupReorderBuffer(void)
25992651 * ok, has to be a surviving logical slot, iterate and delete
26002652 * everything starting with xid-*
26012653 */
2602- sprintf (path , "pg_replslot/%s" , logical_de -> d_name );
2603-
2604- /* we're only creating directories here, skip if it's not our's */
2605- if (lstat (path , & statbuf ) == 0 && !S_ISDIR (statbuf .st_mode ))
2606- continue ;
2607-
2608- spill_dir = AllocateDir (path );
2609- while ((spill_de = ReadDir (spill_dir , path )) != NULL )
2610- {
2611- if (strcmp (spill_de -> d_name , "." ) == 0 ||
2612- strcmp (spill_de -> d_name , ".." ) == 0 )
2613- continue ;
2614-
2615- /* only look at names that can be ours */
2616- if (strncmp (spill_de -> d_name , "xid" , 3 ) == 0 )
2617- {
2618- sprintf (path , "pg_replslot/%s/%s" , logical_de -> d_name ,
2619- spill_de -> d_name );
2620-
2621- if (unlink (path ) != 0 )
2622- ereport (PANIC ,
2623- (errcode_for_file_access (),
2624- errmsg ("could not remove file \"%s\": %m" ,
2625- path )));
2626- }
2627- }
2628- FreeDir (spill_dir );
2654+ ReorderBufferCleanupSerializedTXNs (logical_de -> d_name );
26292655 }
26302656 FreeDir (logical_dir );
26312657}
0 commit comments