@@ -199,6 +199,9 @@ static Size ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn
199
199
static void ReorderBufferRestoreChange (ReorderBuffer * rb , ReorderBufferTXN * txn ,
200
200
char * change );
201
201
static void ReorderBufferRestoreCleanup (ReorderBuffer * rb , ReorderBufferTXN * txn );
202
+ static void ReorderBufferCleanupSerializedTXNs (const char * slotname );
203
+ static void ReorderBufferSerializedPath (char * path , ReplicationSlot * slot ,
204
+ TransactionId xid , XLogSegNo segno );
202
205
203
206
static void ReorderBufferFreeSnap (ReorderBuffer * rb , Snapshot snap );
204
207
static Snapshot ReorderBufferCopySnap (ReorderBuffer * rb , Snapshot orig_snap ,
@@ -217,7 +220,8 @@ static void ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *t
217
220
218
221
219
222
/*
220
- * Allocate a new ReorderBuffer
223
+ * Allocate a new ReorderBuffer and clean out any old serialized state from
224
+ * prior ReorderBuffer instances for the same slot.
221
225
*/
222
226
ReorderBuffer *
223
227
ReorderBufferAllocate (void )
@@ -226,6 +230,8 @@ ReorderBufferAllocate(void)
226
230
HASHCTL hash_ctl ;
227
231
MemoryContext new_ctx ;
228
232
233
+ Assert (MyReplicationSlot != NULL );
234
+
229
235
/* allocate memory in own context, to have better accountability */
230
236
new_ctx = AllocSetContextCreate (CurrentMemoryContext ,
231
237
"ReorderBuffer" ,
@@ -268,6 +274,13 @@ ReorderBufferAllocate(void)
268
274
dlist_init (& buffer -> toplevel_by_lsn );
269
275
slist_init (& buffer -> cached_tuplebufs );
270
276
277
+ /*
278
+ * Ensure there's no stale data from prior uses of this slot, in case some
279
+ * prior exit avoided calling ReorderBufferFree. Failure to do this can
280
+ * produce duplicated txns, and it's very cheap if there's nothing there.
281
+ */
282
+ ReorderBufferCleanupSerializedTXNs (NameStr (MyReplicationSlot -> data .name ));
283
+
271
284
return buffer ;
272
285
}
273
286
@@ -284,6 +297,9 @@ ReorderBufferFree(ReorderBuffer *rb)
284
297
* memory context.
285
298
*/
286
299
MemoryContextDelete (context );
300
+
301
+ /* Free disk space used by unconsumed reorder buffers */
302
+ ReorderBufferCleanupSerializedTXNs (NameStr (MyReplicationSlot -> data .name ));
287
303
}
288
304
289
305
/*
@@ -2073,7 +2089,6 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
2073
2089
int fd = -1 ;
2074
2090
XLogSegNo curOpenSegNo = 0 ;
2075
2091
Size spilled = 0 ;
2076
- char path [MAXPGPATH ];
2077
2092
2078
2093
elog (DEBUG2 , "spill %u changes in XID %u to disk" ,
2079
2094
(uint32 ) txn -> nentries_mem , txn -> xid );
@@ -2100,21 +2115,19 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
2100
2115
*/
2101
2116
if (fd == -1 || !XLByteInSeg (change -> lsn , curOpenSegNo ))
2102
2117
{
2103
- XLogRecPtr recptr ;
2118
+ char path [ MAXPGPATH ] ;
2104
2119
2105
2120
if (fd != -1 )
2106
2121
CloseTransientFile (fd );
2107
2122
2108
2123
XLByteToSeg (change -> lsn , curOpenSegNo );
2109
- XLogSegNoOffsetToRecPtr (curOpenSegNo , 0 , recptr );
2110
2124
2111
2125
/*
2112
2126
* No need to care about TLIs here, only used during a single run,
2113
2127
* so each LSN only maps to a specific WAL record.
2114
2128
*/
2115
- sprintf (path , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2116
- NameStr (MyReplicationSlot -> data .name ), txn -> xid ,
2117
- (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2129
+ ReorderBufferSerializedPath (path , MyReplicationSlot , txn -> xid ,
2130
+ curOpenSegNo );
2118
2131
2119
2132
/* open segment, create it if necessary */
2120
2133
fd = OpenTransientFile (path ,
@@ -2124,8 +2137,7 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
2124
2137
if (fd < 0 )
2125
2138
ereport (ERROR ,
2126
2139
(errcode_for_file_access (),
2127
- errmsg ("could not open file \"%s\": %m" ,
2128
- path )));
2140
+ errmsg ("could not open file \"%s\": %m" , path )));
2129
2141
}
2130
2142
2131
2143
ReorderBufferSerializeChange (rb , txn , fd , change );
@@ -2343,25 +2355,20 @@ ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn,
2343
2355
2344
2356
if (* fd == -1 )
2345
2357
{
2346
- XLogRecPtr recptr ;
2347
2358
char path [MAXPGPATH ];
2348
2359
2349
2360
/* first time in */
2350
2361
if (* segno == 0 )
2351
- {
2352
2362
XLByteToSeg (txn -> first_lsn , * segno );
2353
- }
2354
2363
2355
2364
Assert (* segno != 0 || dlist_is_empty (& txn -> changes ));
2356
- XLogSegNoOffsetToRecPtr (* segno , 0 , recptr );
2357
2365
2358
2366
/*
2359
2367
* No need to care about TLIs here, only used during a single run,
2360
2368
* so each LSN only maps to a specific WAL record.
2361
2369
*/
2362
- sprintf (path , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2363
- NameStr (MyReplicationSlot -> data .name ), txn -> xid ,
2364
- (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2370
+ ReorderBufferSerializedPath (path , MyReplicationSlot , txn -> xid ,
2371
+ * segno );
2365
2372
2366
2373
* fd = OpenTransientFile (path , O_RDONLY | PG_BINARY , 0 );
2367
2374
if (* fd < 0 && errno == ENOENT )
@@ -2597,20 +2604,72 @@ ReorderBufferRestoreCleanup(ReorderBuffer *rb, ReorderBufferTXN *txn)
2597
2604
for (cur = first ; cur <= last ; cur ++ )
2598
2605
{
2599
2606
char path [MAXPGPATH ];
2600
- XLogRecPtr recptr ;
2601
-
2602
- XLogSegNoOffsetToRecPtr (cur , 0 , recptr );
2603
2607
2604
- sprintf (path , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2605
- NameStr (MyReplicationSlot -> data .name ), txn -> xid ,
2606
- (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2608
+ ReorderBufferSerializedPath (path , MyReplicationSlot , txn -> xid , cur );
2607
2609
if (unlink (path ) != 0 && errno != ENOENT )
2608
2610
ereport (ERROR ,
2609
2611
(errcode_for_file_access (),
2610
2612
errmsg ("could not remove file \"%s\": %m" , path )));
2611
2613
}
2612
2614
}
2613
2615
2616
+ /*
2617
+ * Remove any leftover serialized reorder buffers from a slot directory after a
2618
+ * prior crash or decoding session exit.
2619
+ */
2620
+ static void
2621
+ ReorderBufferCleanupSerializedTXNs (const char * slotname )
2622
+ {
2623
+ DIR * spill_dir ;
2624
+ struct dirent * spill_de ;
2625
+ struct stat statbuf ;
2626
+ char path [MAXPGPATH * 2 + 12 ];
2627
+
2628
+ sprintf (path , "pg_replslot/%s" , slotname );
2629
+
2630
+ /* we're only handling directories here, skip if it's not ours */
2631
+ if (lstat (path , & statbuf ) == 0 && !S_ISDIR (statbuf .st_mode ))
2632
+ return ;
2633
+
2634
+ spill_dir = AllocateDir (path );
2635
+ while ((spill_de = ReadDirExtended (spill_dir , path , INFO )) != NULL )
2636
+ {
2637
+ /* only look at names that can be ours */
2638
+ if (strncmp (spill_de -> d_name , "xid" , 3 ) == 0 )
2639
+ {
2640
+ snprintf (path , sizeof (path ),
2641
+ "pg_replslot/%s/%s" , slotname ,
2642
+ spill_de -> d_name );
2643
+
2644
+ if (unlink (path ) != 0 )
2645
+ ereport (ERROR ,
2646
+ (errcode_for_file_access (),
2647
+ errmsg ("could not remove file \"%s\" during removal of pg_replslot/%s/*.xid: %m" ,
2648
+ path , slotname )));
2649
+ }
2650
+ }
2651
+ FreeDir (spill_dir );
2652
+ }
2653
+
2654
+ /*
2655
+ * Given a replication slot, transaction ID and segment number, fill in the
2656
+ * corresponding spill file into 'path', which is a caller-owned buffer of size
2657
+ * at least MAXPGPATH.
2658
+ */
2659
+ static void
2660
+ ReorderBufferSerializedPath (char * path , ReplicationSlot * slot , TransactionId xid ,
2661
+ XLogSegNo segno )
2662
+ {
2663
+ XLogRecPtr recptr ;
2664
+
2665
+ XLogSegNoOffsetToRecPtr (segno , 0 , recptr );
2666
+
2667
+ snprintf (path , MAXPGPATH , "pg_replslot/%s/xid-%u-lsn-%X-%X.snap" ,
2668
+ NameStr (MyReplicationSlot -> data .name ),
2669
+ xid ,
2670
+ (uint32 ) (recptr >> 32 ), (uint32 ) recptr );
2671
+ }
2672
+
2614
2673
/*
2615
2674
* Delete all data spilled to disk after we've restarted/crashed. It will be
2616
2675
* recreated when the respective slots are reused.
@@ -2621,15 +2680,9 @@ StartupReorderBuffer(void)
2621
2680
DIR * logical_dir ;
2622
2681
struct dirent * logical_de ;
2623
2682
2624
- DIR * spill_dir ;
2625
- struct dirent * spill_de ;
2626
-
2627
2683
logical_dir = AllocateDir ("pg_replslot" );
2628
2684
while ((logical_de = ReadDir (logical_dir , "pg_replslot" )) != NULL )
2629
2685
{
2630
- struct stat statbuf ;
2631
- char path [MAXPGPATH * 2 + 12 ];
2632
-
2633
2686
if (strcmp (logical_de -> d_name , "." ) == 0 ||
2634
2687
strcmp (logical_de -> d_name , ".." ) == 0 )
2635
2688
continue ;
@@ -2642,33 +2695,7 @@ StartupReorderBuffer(void)
2642
2695
* ok, has to be a surviving logical slot, iterate and delete
2643
2696
* everything starting with xid-*
2644
2697
*/
2645
- sprintf (path , "pg_replslot/%s" , logical_de -> d_name );
2646
-
2647
- /* we're only creating directories here, skip if it's not our's */
2648
- if (lstat (path , & statbuf ) == 0 && !S_ISDIR (statbuf .st_mode ))
2649
- continue ;
2650
-
2651
- spill_dir = AllocateDir (path );
2652
- while ((spill_de = ReadDir (spill_dir , path )) != NULL )
2653
- {
2654
- if (strcmp (spill_de -> d_name , "." ) == 0 ||
2655
- strcmp (spill_de -> d_name , ".." ) == 0 )
2656
- continue ;
2657
-
2658
- /* only look at names that can be ours */
2659
- if (strncmp (spill_de -> d_name , "xid" , 3 ) == 0 )
2660
- {
2661
- sprintf (path , "pg_replslot/%s/%s" , logical_de -> d_name ,
2662
- spill_de -> d_name );
2663
-
2664
- if (unlink (path ) != 0 )
2665
- ereport (PANIC ,
2666
- (errcode_for_file_access (),
2667
- errmsg ("could not remove file \"%s\": %m" ,
2668
- path )));
2669
- }
2670
- }
2671
- FreeDir (spill_dir );
2698
+ ReorderBufferCleanupSerializedTXNs (logical_de -> d_name );
2672
2699
}
2673
2700
FreeDir (logical_dir );
2674
2701
}
0 commit comments