aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKevin Grittner <kgrittn@postgresql.org>2015-09-11 13:06:51 -0500
committerKevin Grittner <kgrittn@postgresql.org>2015-09-11 13:06:51 -0500
commit5ddc72887a012f6a8b85707ef27d85c274faf53d (patch)
tree394437d186299d95d7298a4c971d79a531e0ca8f /src
parentaa65de042f5828968f2f6cd65f45c543a40cc3e6 (diff)
downloadpostgresql-5ddc72887a012f6a8b85707ef27d85c274faf53d.tar.gz
postgresql-5ddc72887a012f6a8b85707ef27d85c274faf53d.zip
Fix an O(N^2) problem in foreign key references.
Commit 45ba424f improved foreign key lookups during bulk updates when the FK value does not change. When restoring a schema dump from a database with many (say 100,000) foreign keys, this cache would grow very big and every ALTER TABLE command was causing an InvalidateConstraintCacheCallBack(), which uses a sequential hash table scan. This could cause a severe performance regression in restoring a schema dump (including during pg_upgrade). The patch uses a heuristic method of detecting when the hash table should be destroyed and recreated. InvalidateConstraintCacheCallBack() adds the current size of the hash table to a counter. When that sum reaches 1,000,000, the hash table is flushed. This fixes the regression without noticeable harm to the bulk update use case. Jan Wieck Backpatch to 9.3 where the performance regression was introduced.
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/ri_triggers.c38
1 files changed, 35 insertions, 3 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 61edde9c5d3..0469522a567 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -183,6 +183,7 @@ typedef struct RI_CompareHashEntry
* ----------
*/
static HTAB *ri_constraint_cache = NULL;
+static long ri_constraint_cache_seq_count = 0;
static HTAB *ri_query_cache = NULL;
static HTAB *ri_compare_cache = NULL;
@@ -215,6 +216,7 @@ static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum oldvalue, Datum newvalue);
+static void ri_InitConstraintCache(void);
static void ri_InitHashTables(void);
static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
@@ -2945,6 +2947,20 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
Assert(ri_constraint_cache != NULL);
+ /*
+ * Prevent an O(N^2) problem when creating large amounts of foreign
+ * key constraints with ALTER TABLE, like it happens at the end of
+ * a pg_dump with hundred-thousands of tables having references.
+ */
+ ri_constraint_cache_seq_count += hash_get_num_entries(ri_constraint_cache);
+ if (ri_constraint_cache_seq_count > 1000000)
+ {
+ hash_destroy(ri_constraint_cache);
+ ri_InitConstraintCache();
+ ri_constraint_cache_seq_count = 0;
+ return;
+ }
+
hash_seq_init(&status, ri_constraint_cache);
while ((hentry = (RI_ConstraintInfo *) hash_seq_search(&status)) != NULL)
{
@@ -3364,13 +3380,15 @@ ri_NullCheck(HeapTuple tup,
/* ----------
- * ri_InitHashTables -
+ * ri_InitConstraintCache
*
- * Initialize our internal hash tables.
+ * Initialize ri_constraint_cache when new or being rebuilt.
+ *
+ * This needs to be done from two places, so split it out to prevent drift.
* ----------
*/
static void
-ri_InitHashTables(void)
+ri_InitConstraintCache(void)
{
HASHCTL ctl;
@@ -3380,6 +3398,20 @@ ri_InitHashTables(void)
ri_constraint_cache = hash_create("RI constraint cache",
RI_INIT_CONSTRAINTHASHSIZE,
&ctl, HASH_ELEM | HASH_BLOBS);
+}
+
+/* ----------
+ * ri_InitHashTables -
+ *
+ * Initialize our internal hash tables.
+ * ----------
+ */
+static void
+ri_InitHashTables(void)
+{
+ HASHCTL ctl;
+
+ ri_InitConstraintCache();
/* Arrange to flush cache on pg_constraint changes */
CacheRegisterSyscacheCallback(CONSTROID,