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..7f6c6b67cf 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, reset the oncommit Bloom filter. */ + on_commits_filter_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..662d163976 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,14 @@ typedef struct OnCommitItem static List *on_commits = NIL; +/* + * Filter out the temporary tables accessed in the current transaction. + * For a Bloom filter with 128 items and a size of 1KB, the false positive + * rate is far less than 1%. + */ +#define ON_COMMITS_FILTER_ITEMS 128 +#define ON_COMMITS_FILTER_BYTES (1 * 1024) +static bloom_filter* on_commits_filter = NULL; /* * State information for ALTER TABLE @@ -17429,7 +17438,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 +20791,56 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); } + +/* + * on_commit_filter_init: Allocate memory in CacheMemoryContext to initialize + * the oncommit filter. It is called during process initialization. + */ +void on_commits_filter_init(void) +{ + MemoryContext oldcxt; + + Assert(on_commits_filter == NULL); + + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + on_commits_filter = + bloom_create_ex(ON_COMMITS_FILTER_ITEMS, work_mem, 0, ON_COMMITS_FILTER_BYTES); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * on_commits_filter_reset: Reset the Bloom filter without reallocating memory, + * simply by resetting the bitset. It is called when starting a new transaction, + * that is, when the XACT_FLAGS_ACCESSEDTEMPNAMESPACE flag is cleared. + */ +void on_commits_filter_reset(void) +{ + Assert(on_commits_filter != NULL); + + bloom_clear(on_commits_filter); +} + +/* + * 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) +{ + Assert(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. + */ +bool on_commits_filter_contains(Oid relid) +{ + Assert(on_commits_filter != NULL); + + 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..3650255782 100644 --- a/src/backend/lib/bloomfilter.c +++ b/src/backend/lib/bloomfilter.c @@ -40,6 +40,7 @@ #include "port/pg_bitutils.h" #define MAX_HASH_FUNCS 10 +#define MIN_BITSET_BYTES (1024 * 1024) struct bloom_filter { @@ -85,6 +86,13 @@ static inline uint32 mod_m(uint32 val, uint64 m); */ bloom_filter * bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed) +{ + return bloom_create_ex(total_elems, bloom_work_mem, seed, MIN_BITSET_BYTES); +} + +bloom_filter * +bloom_create_ex(int64 total_elems, int bloom_work_mem, uint64 seed, + uint64 min_bitset_bytes) { bloom_filter *filter; int bloom_power; @@ -292,3 +300,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/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 0805398e24..ccdd9e7af8 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -33,6 +33,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_db_role_setting.h" #include "catalog/pg_tablespace.h" +#include "commands/tablecmds.h" #include "libpq/auth.h" #include "libpq/libpq-be.h" #include "mb/pg_wchar.h" @@ -818,6 +819,9 @@ InitPostgres(const char *in_dbname, Oid dboid, InitCatalogCache(); InitPlanCache(); + /* Initialize on_commits filter */ + on_commits_filter_init(); + /* Initialize portal manager */ EnablePortalManager(); diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 85cbad3d0c..29fb74d6ab 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -107,4 +107,9 @@ extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, extern bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint); +extern void on_commits_filter_init(void); +extern void on_commits_filter_reset(void); +extern void on_commits_filter_add(Oid relid); +extern bool on_commits_filter_contains(Oid relid); + #endif /* TABLECMDS_H */ diff --git a/src/include/lib/bloomfilter.h b/src/include/lib/bloomfilter.h index 6ec7173843..13a0670e92 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_ex(int64 total_elems, int bloom_work_mem, + uint64 seed, uint64 min_bitset_bytes); 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 */