v1-0003-Optimize-commit-with-temp-tables-guc.patch
application/octet-stream
Filename: v1-0003-Optimize-commit-with-temp-tables-guc.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v1-0003
| File | + | − |
|---|---|---|
| src/backend/access/common/relation.c | 6 | 0 |
| src/backend/access/transam/xact.c | 3 | 0 |
| src/backend/commands/lockcmds.c | 3 | 0 |
| src/backend/commands/tablecmds.c | 93 | 1 |
| src/backend/lib/bloomfilter.c | 41 | 0 |
| src/backend/utils/misc/guc_tables.c | 11 | 0 |
| src/include/commands/tablecmds.h | 4 | 0 |
| src/include/lib/bloomfilter.h | 3 | 0 |
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..54eaef3e51 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,17 @@ typedef struct OnCommitItem
static List *on_commits = NIL;
+/*
+ * Filter out the temporary tables accessed in the current transaction.
+ * The threshold for using the filter is configured as a GUC parameter for easy
+ * adjustment in different scenarios.
+ * 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_KB 1
+int on_commits_filter_threshold = 10;
+static bloom_filter* on_commits_filter = NULL;
/*
* State information for ALTER TABLE
@@ -662,6 +674,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 +17442,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 +20795,66 @@ 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 (on_commits_filter_threshold < 0 ||
+ 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/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index d28b0bcb40..0a2e3bf4b6 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -39,6 +39,7 @@
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/event_trigger.h"
+#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/user.h"
@@ -3649,6 +3650,16 @@ struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"on_commits_filter_threshold", PGC_USERSET, CUSTOM_OPTIONS,
+ gettext_noop("Minimum number of OnCommitItem to utilize bloom filter optimization, or negative to disable."),
+ NULL,
+ },
+ &on_commits_filter_threshold,
+ 2, -1, 10000,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 85cbad3d0c..56598a240d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -107,4 +107,8 @@ extern void RangeVarCallbackOwnsRelation(const RangeVar *relation,
extern bool PartConstraintImpliedByRelConstraint(Relation scanrel,
List *partConstraint);
+extern int on_commits_filter_threshold;
+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 */