diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c index d8a313a2c9..74b1415ad5 100644 --- a/src/backend/access/common/relation.c +++ b/src/backend/access/common/relation.c @@ -70,7 +70,10 @@ relation_open(Oid relationId, LOCKMODE lockmode) /* Make note that we've accessed a temporary relation */ if (RelationUsesLocalBuffers(r)) + { MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; + on_commits_filter_add(relationId); + } pgstat_init_relation(r); @@ -120,7 +123,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode) /* Make note that we've accessed a temporary relation */ if (RelationUsesLocalBuffers(r)) + { MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; + on_commits_filter_add(relationId); + } pgstat_init_relation(r); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index d119ab909d..dc7bde98ab 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2122,6 +2122,9 @@ StartTransaction(void) forceSyncCommit = false; MyXactFlags = 0; + /* When starting a new transaction, init or reset the oncommit filter. */ + on_commits_filter_init_or_reset(); + /* * reinitialize within-transaction counters */ diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index cd20ae76ba..77ad85576d 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -98,7 +98,10 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, */ relpersistence = get_rel_persistence(relid); if (relpersistence == RELPERSISTENCE_TEMP) + { MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; + on_commits_filter_add(relid); + } /* Check permissions. */ aclresult = LockTableAclCheck(relid, lockmode, GetUserId()); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dbfe0d6b1c..e3695cee16 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -68,6 +68,7 @@ #include "executor/executor.h" #include "foreign/fdwapi.h" #include "foreign/foreign.h" +#include "lib/bloomfilter.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -128,6 +129,15 @@ typedef struct OnCommitItem static List *on_commits = NIL; +/* + * Filter out the temporary tables accessed in the current transaction. + * The number of bits for the bloom filter is fixed at 1KB, which provides a + * good false positive rate even with 1000 items. + */ +#define ON_COMMITS_FILTER_K_HASH_FUNCS 2 +#define ON_COMMITS_FILTER_THRESHOLD 2 +#define ON_COMMITS_FILTER_KB 1 +static bloom_filter* on_commits_filter = NULL; /* * State information for ALTER TABLE @@ -662,6 +672,7 @@ static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, AlterTableUtilityContext *context); static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context); +static bool on_commits_filter_contains(Oid relid); /* ---------------------------------------------------------------- * DefineRelation @@ -17429,7 +17440,23 @@ PreCommit_on_commit_actions(void) * tables, as they must still be empty. */ if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE)) - oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); + { + if (on_commits_filter_contains(oc->relid)) + oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); +#ifdef USE_ASSERT_CHECKING + else + { + /* + * All temporary tables not in the filter should either + * have no physical files or a file size of zero. + */ + Relation rel; + rel = table_open(oc->relid, AccessShareLock); + Assert(!rel->rd_smgr || !smgrexists(rel->rd_smgr, MAIN_FORKNUM) || smgrnblocks(rel->rd_smgr, MAIN_FORKNUM) == 0); + table_close(rel, AccessShareLock); + } +#endif + } break; case ONCOMMIT_DROP: oids_to_drop = lappend_oid(oids_to_drop, oc->relid); @@ -20766,3 +20793,65 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); } + +/* + * on_commits_filter_init_or_reset: Initialize or reset the bloom filter, + * ensuring it is only initialized when the number of on_commits exceeds the + * ON_COMMITS_FILTER_THRESHOLD. It is called when starting a new transaction, + * that is, when the XACT_FLAGS_ACCESSEDTEMPNAMESPACE flag is cleared. + */ +void +on_commits_filter_init_or_reset(void) +{ + if (list_length(on_commits) < ON_COMMITS_FILTER_THRESHOLD) + { + if (on_commits_filter != NULL) + { + bloom_free(on_commits_filter); + on_commits_filter = NULL; + } + return; + } + if (on_commits_filter != NULL) + bloom_clear(on_commits_filter); + else + { + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + on_commits_filter = + bloom_create_v2(ON_COMMITS_FILTER_K_HASH_FUNCS, ON_COMMITS_FILTER_KB, 0); + MemoryContextSwitchTo(oldcxt); + } +} + +/* + * on_commits_filter_add: Add the temporary table OId to the Bloom filter, + * indicating that the current transaction has accessed this temporary table, + * which needs to be truncated upon commit. It is called when the temporary + * table is opened. + */ +void +on_commits_filter_add(Oid relid) +{ + if (on_commits_filter != NULL) + bloom_add_element(on_commits_filter, (unsigned char *) &relid, sizeof(relid)); +} + +/* + * on_commits_filter_contains: Determine whether the specified temporary table + * has been accessed in the current transaction. + */ +static bool +on_commits_filter_contains(Oid relid) +{ + /* + * If the on_commits filter is not initialized, assuming that the current + * transaction has accessed the temp table defaultly, keeping the logic + * consistent with when there is no filter. + */ + if (on_commits_filter == NULL) + return true; + + return !bloom_lacks_element(on_commits_filter, (unsigned char *) &relid, sizeof(relid)); +} diff --git a/src/backend/lib/bloomfilter.c b/src/backend/lib/bloomfilter.c index 360d21ca45..c33ffd1d3c 100644 --- a/src/backend/lib/bloomfilter.c +++ b/src/backend/lib/bloomfilter.c @@ -119,6 +119,42 @@ bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed) return filter; } +/* + * Create Bloom filter with a specified memory size and specified number of + * hash functions in the caller's memory context. + * bloom_work_mem is sized in KB, in line with the general work_mem convention. + * This determines the size of the underlying bitset (trivial bookkeeping space + * isn't counted). + */ +bloom_filter * +bloom_create_v2(int64 k_hash_funcs, int bloom_work_mem, uint64 seed) +{ + bloom_filter *filter; + int bloom_power; + uint64 bitset_bytes; + uint64 bitset_bits; + + Assert(bloom_work_mem > 0); + bitset_bytes = bloom_work_mem * UINT64CONST(1024); + + /* + * Size in bits should be the highest power of two <= target. bitset_bits + * is uint64 because PG_UINT32_MAX is 2^32 - 1, not 2^32 + */ + bloom_power = my_bloom_power(bitset_bytes * BITS_PER_BYTE); + bitset_bits = UINT64CONST(1) << bloom_power; + bitset_bytes = bitset_bits / BITS_PER_BYTE; + + /* Allocate bloom filter with unset bitset */ + filter = palloc0(offsetof(bloom_filter, bitset) + + sizeof(unsigned char) * bitset_bytes); + filter->k_hash_funcs = k_hash_funcs; + filter->seed = seed; + filter->m = bitset_bits; + + return filter; +} + /* * Free Bloom filter */ @@ -292,3 +328,8 @@ mod_m(uint32 val, uint64 m) return val & (m - 1); } + +void bloom_clear(bloom_filter *filter) +{ + memset(filter->bitset, 0, filter->m / BITS_PER_BYTE); +} \ No newline at end of file diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 85cbad3d0c..461bc1ea47 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -107,4 +107,7 @@ extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, extern bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint); +extern void on_commits_filter_init_or_reset(void); +extern void on_commits_filter_add(Oid relid); + #endif /* TABLECMDS_H */ diff --git a/src/include/lib/bloomfilter.h b/src/include/lib/bloomfilter.h index 6ec7173843..11b0ab1264 100644 --- a/src/include/lib/bloomfilter.h +++ b/src/include/lib/bloomfilter.h @@ -17,11 +17,14 @@ typedef struct bloom_filter bloom_filter; extern bloom_filter *bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed); +extern bloom_filter *bloom_create_v2(int64 k_hash_funcs, int bloom_work_mem, + uint64 seed); extern void bloom_free(bloom_filter *filter); extern void bloom_add_element(bloom_filter *filter, unsigned char *elem, size_t len); extern bool bloom_lacks_element(bloom_filter *filter, unsigned char *elem, size_t len); extern double bloom_prop_bits_set(bloom_filter *filter); +extern void bloom_clear(bloom_filter *filter); #endif /* BLOOMFILTER_H */