@@ -137,9 +137,27 @@ static long relcacheInvalsReceived = 0L;
137
137
static List * initFileRelationIds = NIL ;
138
138
139
139
/*
140
- * This flag lets us optimize away work in AtEO(Sub)Xact_RelationCache().
140
+ * eoxact_list[] stores the OIDs of relations that (might) need AtEOXact
141
+ * cleanup work. This list intentionally has limited size; if it overflows,
142
+ * we fall back to scanning the whole hashtable. There is no value in a very
143
+ * large list because (1) at some point, a hash_seq_search scan is faster than
144
+ * retail lookups, and (2) the value of this is to reduce EOXact work for
145
+ * short transactions, which can't have dirtied all that many tables anyway.
146
+ * EOXactListAdd() does not bother to prevent duplicate list entries, so the
147
+ * cleanup processing must be idempotent.
141
148
*/
142
- static bool need_eoxact_work = false;
149
+ #define MAX_EOXACT_LIST 32
150
+ static Oid eoxact_list [MAX_EOXACT_LIST ];
151
+ static int eoxact_list_len = 0 ;
152
+ static bool eoxact_list_overflowed = false;
153
+
154
+ #define EOXactListAdd (rel ) \
155
+ do { \
156
+ if (eoxact_list_len < MAX_EOXACT_LIST) \
157
+ eoxact_list[eoxact_list_len++] = (rel)->rd_id; \
158
+ else \
159
+ eoxact_list_overflowed = true; \
160
+ } while (0)
143
161
144
162
145
163
/*
@@ -204,6 +222,9 @@ static void RelationClearRelation(Relation relation, bool rebuild);
204
222
205
223
static void RelationReloadIndexInfo (Relation relation );
206
224
static void RelationFlushRelation (Relation relation );
225
+ static void AtEOXact_cleanup (Relation relation , bool isCommit );
226
+ static void AtEOSubXact_cleanup (Relation relation , bool isCommit ,
227
+ SubTransactionId mySubid , SubTransactionId parentSubid );
207
228
static bool load_relcache_init_file (bool shared );
208
229
static void write_relcache_init_file (bool shared );
209
230
static void write_item (const void * data , Size len , FILE * fp );
@@ -2275,38 +2296,69 @@ AtEOXact_RelationCache(bool isCommit)
2275
2296
{
2276
2297
HASH_SEQ_STATUS status ;
2277
2298
RelIdCacheEnt * idhentry ;
2299
+ int i ;
2278
2300
2279
2301
/*
2280
- * To speed up transaction exit, we want to avoid scanning the relcache
2281
- * unless there is actually something for this routine to do. Other than
2282
- * the debug-only Assert checks, most transactions don't create any work
2283
- * for us to do here, so we keep a static flag that gets set if there is
2284
- * anything to do. (Currently, this means either a relation is created in
2285
- * the current xact, or one is given a new relfilenode, or an index list
2286
- * is forced.) For simplicity, the flag remains set till end of top-level
2287
- * transaction, even though we could clear it at subtransaction end in
2288
- * some cases.
2289
- */
2290
- if (!need_eoxact_work
2291
- #ifdef USE_ASSERT_CHECKING
2292
- && !assert_enabled
2293
- #endif
2294
- )
2295
- return ;
2296
-
2297
- hash_seq_init (& status , RelationIdCache );
2298
-
2299
- while ((idhentry = (RelIdCacheEnt * ) hash_seq_search (& status )) != NULL )
2302
+ * Unless the eoxact_list[] overflowed, we only need to examine the rels
2303
+ * listed in it. Otherwise fall back on a hash_seq_search scan.
2304
+ *
2305
+ * For simplicity, eoxact_list[] entries are not deleted till end of
2306
+ * top-level transaction, even though we could remove them at
2307
+ * subtransaction end in some cases, or remove relations from the list if
2308
+ * they are cleared for other reasons. Therefore we should expect the
2309
+ * case that list entries are not found in the hashtable; if not, there's
2310
+ * nothing to do for them.
2311
+ */
2312
+ if (eoxact_list_overflowed )
2300
2313
{
2301
- Relation relation = idhentry -> reldesc ;
2314
+ hash_seq_init (& status , RelationIdCache );
2315
+ while ((idhentry = (RelIdCacheEnt * ) hash_seq_search (& status )) != NULL )
2316
+ {
2317
+ AtEOXact_cleanup (idhentry -> reldesc , isCommit );
2318
+ }
2319
+ }
2320
+ else
2321
+ {
2322
+ for (i = 0 ; i < eoxact_list_len ; i ++ )
2323
+ {
2324
+ idhentry = (RelIdCacheEnt * ) hash_search (RelationIdCache ,
2325
+ (void * ) & eoxact_list [i ],
2326
+ HASH_FIND ,
2327
+ NULL );
2328
+ if (idhentry != NULL )
2329
+ AtEOXact_cleanup (idhentry -> reldesc , isCommit );
2330
+ }
2331
+ }
2302
2332
2333
+ /* Now we're out of the transaction and can clear the list */
2334
+ eoxact_list_len = 0 ;
2335
+ eoxact_list_overflowed = false;
2336
+ }
2337
+
2338
+ /*
2339
+ * AtEOXact_cleanup
2340
+ *
2341
+ * Clean up a single rel at main-transaction commit or abort
2342
+ *
2343
+ * NB: this processing must be idempotent, because EOXactListAdd() doesn't
2344
+ * bother to prevent duplicate entries in eoxact_list[].
2345
+ */
2346
+ static void
2347
+ AtEOXact_cleanup (Relation relation , bool isCommit )
2348
+ {
2303
2349
/*
2304
2350
* The relcache entry's ref count should be back to its normal
2305
2351
* not-in-a-transaction state: 0 unless it's nailed in cache.
2306
2352
*
2307
2353
* In bootstrap mode, this is NOT true, so don't check it --- the
2308
2354
* bootstrap code expects relations to stay open across start/commit
2309
2355
* transaction calls. (That seems bogus, but it's not worth fixing.)
2356
+ *
2357
+ * Note: ideally this check would be applied to every relcache entry,
2358
+ * not just those that have eoxact work to do. But it's not worth
2359
+ * forcing a scan of the whole relcache just for this. (Moreover,
2360
+ * doing so would mean that assert-enabled testing never tests the
2361
+ * hash_search code path above, which seems a bad idea.)
2310
2362
*/
2311
2363
#ifdef USE_ASSERT_CHECKING
2312
2364
if (!IsBootstrapProcessingMode ())
@@ -2335,7 +2387,7 @@ AtEOXact_RelationCache(bool isCommit)
2335
2387
else
2336
2388
{
2337
2389
RelationClearRelation (relation , false);
2338
- continue ;
2390
+ return ;
2339
2391
}
2340
2392
}
2341
2393
@@ -2354,10 +2406,6 @@ AtEOXact_RelationCache(bool isCommit)
2354
2406
relation -> rd_oidindex = InvalidOid ;
2355
2407
relation -> rd_indexvalid = 0 ;
2356
2408
}
2357
- }
2358
-
2359
- /* Once done with the transaction, we can reset need_eoxact_work */
2360
- need_eoxact_work = false;
2361
2409
}
2362
2410
2363
2411
/*
@@ -2373,20 +2421,51 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
2373
2421
{
2374
2422
HASH_SEQ_STATUS status ;
2375
2423
RelIdCacheEnt * idhentry ;
2424
+ int i ;
2376
2425
2377
2426
/*
2378
- * Skip the relcache scan if nothing to do --- see notes for
2379
- * AtEOXact_RelationCache.
2427
+ * Unless the eoxact_list[] overflowed, we only need to examine the rels
2428
+ * listed in it. Otherwise fall back on a hash_seq_search scan. Same
2429
+ * logic as in AtEOXact_RelationCache.
2380
2430
*/
2381
- if (!need_eoxact_work )
2382
- return ;
2383
-
2384
- hash_seq_init (& status , RelationIdCache );
2385
-
2386
- while ((idhentry = (RelIdCacheEnt * ) hash_seq_search (& status )) != NULL )
2431
+ if (eoxact_list_overflowed )
2387
2432
{
2388
- Relation relation = idhentry -> reldesc ;
2433
+ hash_seq_init (& status , RelationIdCache );
2434
+ while ((idhentry = (RelIdCacheEnt * ) hash_seq_search (& status )) != NULL )
2435
+ {
2436
+ AtEOSubXact_cleanup (idhentry -> reldesc , isCommit ,
2437
+ mySubid , parentSubid );
2438
+ }
2439
+ }
2440
+ else
2441
+ {
2442
+ for (i = 0 ; i < eoxact_list_len ; i ++ )
2443
+ {
2444
+ idhentry = (RelIdCacheEnt * ) hash_search (RelationIdCache ,
2445
+ (void * ) & eoxact_list [i ],
2446
+ HASH_FIND ,
2447
+ NULL );
2448
+ if (idhentry != NULL )
2449
+ AtEOSubXact_cleanup (idhentry -> reldesc , isCommit ,
2450
+ mySubid , parentSubid );
2451
+ }
2452
+ }
2389
2453
2454
+ /* Don't reset the list; we still need more cleanup later */
2455
+ }
2456
+
2457
+ /*
2458
+ * AtEOSubXact_cleanup
2459
+ *
2460
+ * Clean up a single rel at subtransaction commit or abort
2461
+ *
2462
+ * NB: this processing must be idempotent, because EOXactListAdd() doesn't
2463
+ * bother to prevent duplicate entries in eoxact_list[].
2464
+ */
2465
+ static void
2466
+ AtEOSubXact_cleanup (Relation relation , bool isCommit ,
2467
+ SubTransactionId mySubid , SubTransactionId parentSubid )
2468
+ {
2390
2469
/*
2391
2470
* Is it a relation created in the current subtransaction?
2392
2471
*
@@ -2400,7 +2479,7 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
2400
2479
else
2401
2480
{
2402
2481
RelationClearRelation (relation , false);
2403
- continue ;
2482
+ return ;
2404
2483
}
2405
2484
}
2406
2485
@@ -2426,7 +2505,6 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
2426
2505
relation -> rd_oidindex = InvalidOid ;
2427
2506
relation -> rd_indexvalid = 0 ;
2428
2507
}
2429
- }
2430
2508
}
2431
2509
2432
2510
@@ -2516,9 +2594,6 @@ RelationBuildLocalRelation(const char *relname,
2516
2594
rel -> rd_createSubid = GetCurrentSubTransactionId ();
2517
2595
rel -> rd_newRelfilenodeSubid = InvalidSubTransactionId ;
2518
2596
2519
- /* must flag that we have rels created in this transaction */
2520
- need_eoxact_work = true;
2521
-
2522
2597
/*
2523
2598
* create a new tuple descriptor from the one passed in. We do this
2524
2599
* partly to copy it into the cache context, and partly because the new
@@ -2609,6 +2684,12 @@ RelationBuildLocalRelation(const char *relname,
2609
2684
*/
2610
2685
RelationCacheInsert (rel );
2611
2686
2687
+ /*
2688
+ * Flag relation as needing eoxact cleanup (to clear rd_createSubid).
2689
+ * We can't do this before storing relid in it.
2690
+ */
2691
+ EOXactListAdd (rel );
2692
+
2612
2693
/*
2613
2694
* done building relcache entry.
2614
2695
*/
@@ -2732,8 +2813,9 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid)
2732
2813
* operations on the rel in the same transaction.
2733
2814
*/
2734
2815
relation -> rd_newRelfilenodeSubid = GetCurrentSubTransactionId ();
2735
- /* ... and now we have eoxact cleanup work to do */
2736
- need_eoxact_work = true;
2816
+
2817
+ /* Flag relation as needing eoxact cleanup (to remove the hint) */
2818
+ EOXactListAdd (relation );
2737
2819
}
2738
2820
2739
2821
@@ -3513,8 +3595,8 @@ RelationSetIndexList(Relation relation, List *indexIds, Oid oidIndex)
3513
3595
relation -> rd_indexlist = indexIds ;
3514
3596
relation -> rd_oidindex = oidIndex ;
3515
3597
relation -> rd_indexvalid = 2 ; /* mark list as forced */
3516
- /* must flag that we have a forced index list */
3517
- need_eoxact_work = true ;
3598
+ /* Flag relation as needing eoxact cleanup (to reset the list) */
3599
+ EOXactListAdd ( relation ) ;
3518
3600
}
3519
3601
3520
3602
/*
0 commit comments