Remove unstable test suite added by 525392d57
authorAmit Langote <[email protected]>
Sat, 22 Feb 2025 06:19:23 +0000 (15:19 +0900)
committerAmit Langote <[email protected]>
Sat, 22 Feb 2025 06:19:23 +0000 (15:19 +0900)
The 'cached-plan-inval' test suite, introduced in 525392d57 under
src/test/modules/delay_execution, aimed to verify that cached plan
invalidation triggers replanning after deferred locks are taken.
However, its ExecutorStart_hook-based approach relies on lock timing
assumptions that, in retrospect, are fragile. This instability was
exposed by failures on BF animal trilobite, which builds with
CLOBBER_CACHE_ALWAYS.

One option was to dynamically disable the cache behavior that causes
the test suite to fail by setting "debug_discard_caches = 0", but it
seems better to remove the suite. The risk of future failures due to
other cache flush hazards outweighs the benefit of catching real
breakage in the backend behavior it tests.

Reported-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/2990641.1740117879@sss.pgh.pa.us

src/test/modules/delay_execution/Makefile
src/test/modules/delay_execution/delay_execution.c
src/test/modules/delay_execution/expected/cached-plan-inval.out [deleted file]
src/test/modules/delay_execution/meson.build
src/test/modules/delay_execution/specs/cached-plan-inval.spec [deleted file]

index 3eeb097fde4b3a2fae600a5f2d0e8150ddea13f5..70f24e846da1f82d1fc75a273351de1574038ff2 100644 (file)
@@ -8,8 +8,7 @@ OBJS = \
    delay_execution.o
 
 ISOLATION = partition-addition \
-       partition-removal-1 \
-       cached-plan-inval
+       partition-removal-1
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
index ad22bc9f2a812c154e9444fdabbb7aaaaf0a0765..7bc97f84a1c3cf4dfd96818f3b8cff023d3924ec 100644 (file)
@@ -1,18 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * delay_execution.c
- *     Test module to introduce delay at various points during execution of a
- *     query to test that execution proceeds safely in light of concurrent
- *     changes.
+ *     Test module to allow delay between parsing and execution of a query.
  *
  * The delay is implemented by taking and immediately releasing a specified
  * advisory lock.  If another process has previously taken that lock, the
  * current process will be blocked until the lock is released; otherwise,
  * there's no effect.  This allows an isolationtester script to reliably
- * test behaviors where some specified action happens in another backend in
- * a couple of cases: 1) between parsing and execution of any desired query
- * when using the planner_hook, 2) between RevalidateCachedQuery() and
- * ExecutorStart() when using the ExecutorStart_hook.
+ * test behaviors where some specified action happens in another backend
+ * between parsing and execution of any desired query.
  *
  * Copyright (c) 2020-2025, PostgreSQL Global Development Group
  *
@@ -26,7 +22,6 @@
 
 #include <limits.h>
 
-#include "executor/executor.h"
 #include "optimizer/planner.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc.h"
@@ -37,11 +32,9 @@ PG_MODULE_MAGIC;
 
 /* GUC: advisory lock ID to use.  Zero disables the feature. */
 static int post_planning_lock_id = 0;
-static int executor_start_lock_id = 0;
 
-/* Save previous hook users to be a good citizen */
+/* Save previous planner hook user to be a good citizen */
 static planner_hook_type prev_planner_hook = NULL;
-static ExecutorStart_hook_type prev_ExecutorStart_hook = NULL;
 
 
 /* planner_hook function to provide the desired delay */
@@ -77,45 +70,11 @@ delay_execution_planner(Query *parse, const char *query_string,
    return result;
 }
 
-/* ExecutorStart_hook function to provide the desired delay */
-static bool
-delay_execution_ExecutorStart(QueryDesc *queryDesc, int eflags)
-{
-   bool        plan_valid;
-
-   /* If enabled, delay by taking and releasing the specified lock */
-   if (executor_start_lock_id != 0)
-   {
-       DirectFunctionCall1(pg_advisory_lock_int8,
-                           Int64GetDatum((int64) executor_start_lock_id));
-       DirectFunctionCall1(pg_advisory_unlock_int8,
-                           Int64GetDatum((int64) executor_start_lock_id));
-
-       /*
-        * Ensure that we notice any pending invalidations, since the advisory
-        * lock functions don't do this.
-        */
-       AcceptInvalidationMessages();
-   }
-
-   /* Now start the executor, possibly via a previous hook user */
-   if (prev_ExecutorStart_hook)
-       plan_valid = prev_ExecutorStart_hook(queryDesc, eflags);
-   else
-       plan_valid = standard_ExecutorStart(queryDesc, eflags);
-
-   if (executor_start_lock_id != 0)
-       elog(NOTICE, "Finished ExecutorStart(): CachedPlan is %s",
-            plan_valid ? "valid" : "not valid");
-
-   return plan_valid;
-}
-
 /* Module load function */
 void
 _PG_init(void)
 {
-   /* Set up GUCs to control which lock is used */
+   /* Set up the GUC to control which lock is used */
    DefineCustomIntVariable("delay_execution.post_planning_lock_id",
                            "Sets the advisory lock ID to be locked/unlocked after planning.",
                            "Zero disables the delay.",
@@ -128,22 +87,9 @@ _PG_init(void)
                            NULL,
                            NULL);
 
-   DefineCustomIntVariable("delay_execution.executor_start_lock_id",
-                           "Sets the advisory lock ID to be locked/unlocked before starting execution.",
-                           "Zero disables the delay.",
-                           &executor_start_lock_id,
-                           0,
-                           0, INT_MAX,
-                           PGC_USERSET,
-                           0,
-                           NULL,
-                           NULL,
-                           NULL);
    MarkGUCPrefixReserved("delay_execution");
 
-   /* Install our hooks. */
+   /* Install our hook */
    prev_planner_hook = planner_hook;
    planner_hook = delay_execution_planner;
-   prev_ExecutorStart_hook = ExecutorStart_hook;
-   ExecutorStart_hook = delay_execution_ExecutorStart;
 }
diff --git a/src/test/modules/delay_execution/expected/cached-plan-inval.out b/src/test/modules/delay_execution/expected/cached-plan-inval.out
deleted file mode 100644 (file)
index 444e06b..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-Parsed test spec with 2 sessions
-
-starting permutation: s1prep s2lock s1exec s2dropi s2unlock
-step s1prep: SET plan_cache_mode = force_generic_plan;
-         PREPARE q AS SELECT * FROM foov WHERE a = $1 FOR UPDATE;
-         EXPLAIN (COSTS OFF) EXECUTE q (1);
-QUERY PLAN                                           
------------------------------------------------------
-LockRows                                             
-  ->  Append                                         
-        Subplans Removed: 2                          
-        ->  Index Scan using foo1_1_a on foo1_1 foo_1
-              Index Cond: (a = $1)                   
-(5 rows)
-
-step s2lock: SELECT pg_advisory_lock(12345);
-pg_advisory_lock
-----------------
-                
-(1 row)
-
-step s1exec: LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q (1); <waiting ...>
-step s2dropi: DROP INDEX foo1_1_a;
-step s2unlock: SELECT pg_advisory_unlock(12345);
-pg_advisory_unlock
-------------------
-t                 
-(1 row)
-
-step s1exec: <... completed>
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is not valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                          
-------------------------------------
-LockRows                            
-  ->  Append                        
-        Subplans Removed: 2         
-        ->  Seq Scan on foo1_1 foo_1
-              Filter: (a = $1)      
-(5 rows)
-
-
-starting permutation: s1prep2 s2lock s1exec2 s2dropi s2unlock
-step s1prep2: SET plan_cache_mode = force_generic_plan;
-         PREPARE q2 AS SELECT * FROM foov WHERE a = one() or a = two();
-         EXPLAIN (COSTS OFF) EXECUTE q2;
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                                         
----------------------------------------------------
-Append                                             
-  Subplans Removed: 1                              
-  ->  Index Scan using foo1_1_a on foo1_1 foo_1    
-        Index Cond: (a = ANY (ARRAY[one(), two()]))
-  ->  Seq Scan on foo1_2 foo_2                     
-        Filter: ((a = one()) OR (a = two()))       
-(6 rows)
-
-step s2lock: SELECT pg_advisory_lock(12345);
-pg_advisory_lock
-----------------
-                
-(1 row)
-
-step s1exec2: LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q2; <waiting ...>
-step s2dropi: DROP INDEX foo1_1_a;
-step s2unlock: SELECT pg_advisory_unlock(12345);
-pg_advisory_unlock
-------------------
-t                 
-(1 row)
-
-step s1exec2: <... completed>
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is not valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                                  
---------------------------------------------
-Append                                      
-  Subplans Removed: 1                       
-  ->  Seq Scan on foo1_1 foo_1              
-        Filter: ((a = one()) OR (a = two()))
-  ->  Seq Scan on foo1_2 foo_2              
-        Filter: ((a = one()) OR (a = two()))
-(6 rows)
-
-
-starting permutation: s1prep3 s2lock s1exec3 s2dropi s2unlock
-step s1prep3: SET plan_cache_mode = force_generic_plan;
-         PREPARE q3 AS UPDATE foov SET a = a WHERE a = one() or a = two();
-         EXPLAIN (COSTS OFF) EXECUTE q3;
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                                                     
----------------------------------------------------------------
-Nested Loop                                                    
-  ->  Append                                                   
-        Subplans Removed: 1                                    
-        ->  Index Only Scan using foo1_1_a on foo1_1 foo_1     
-              Index Cond: (a = ANY (ARRAY[one(), two()]))      
-        ->  Seq Scan on foo1_2 foo_2                           
-              Filter: ((a = one()) OR (a = two()))             
-  ->  Materialize                                              
-        ->  Append                                             
-              Subplans Removed: 1                              
-              ->  Seq Scan on bar1 bar_1                       
-                    Filter: (a = one())                        
-                                                               
-Update on bar                                                  
-  Update on bar1 bar_1                                         
-  ->  Nested Loop                                              
-        ->  Append                                             
-              Subplans Removed: 1                              
-              ->  Index Scan using foo1_1_a on foo1_1 foo_1    
-                    Index Cond: (a = ANY (ARRAY[one(), two()]))
-              ->  Seq Scan on foo1_2 foo_2                     
-                    Filter: ((a = one()) OR (a = two()))       
-        ->  Materialize                                        
-              ->  Append                                       
-                    Subplans Removed: 1                        
-                    ->  Seq Scan on bar1 bar_1                 
-                          Filter: (a = one())                  
-                                                               
-Update on foo                                                  
-  Update on foo1_1 foo_1                                       
-  Update on foo1_2 foo_2                                       
-  ->  Append                                                   
-        Subplans Removed: 1                                    
-        ->  Index Scan using foo1_1_a on foo1_1 foo_1          
-              Index Cond: (a = ANY (ARRAY[one(), two()]))      
-        ->  Seq Scan on foo1_2 foo_2                           
-              Filter: ((a = one()) OR (a = two()))             
-(37 rows)
-
-step s2lock: SELECT pg_advisory_lock(12345);
-pg_advisory_lock
-----------------
-                
-(1 row)
-
-step s1exec3: LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q3; <waiting ...>
-step s2dropi: DROP INDEX foo1_1_a;
-step s2unlock: SELECT pg_advisory_unlock(12345);
-pg_advisory_unlock
-------------------
-t                 
-(1 row)
-
-step s1exec3: <... completed>
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is not valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                                              
---------------------------------------------------------
-Nested Loop                                             
-  ->  Append                                            
-        Subplans Removed: 1                             
-        ->  Seq Scan on foo1_1 foo_1                    
-              Filter: ((a = one()) OR (a = two()))      
-        ->  Seq Scan on foo1_2 foo_2                    
-              Filter: ((a = one()) OR (a = two()))      
-  ->  Materialize                                       
-        ->  Append                                      
-              Subplans Removed: 1                       
-              ->  Seq Scan on bar1 bar_1                
-                    Filter: (a = one())                 
-                                                        
-Update on bar                                           
-  Update on bar1 bar_1                                  
-  ->  Nested Loop                                       
-        ->  Append                                      
-              Subplans Removed: 1                       
-              ->  Seq Scan on foo1_1 foo_1              
-                    Filter: ((a = one()) OR (a = two()))
-              ->  Seq Scan on foo1_2 foo_2              
-                    Filter: ((a = one()) OR (a = two()))
-        ->  Materialize                                 
-              ->  Append                                
-                    Subplans Removed: 1                 
-                    ->  Seq Scan on bar1 bar_1          
-                          Filter: (a = one())           
-                                                        
-Update on foo                                           
-  Update on foo1_1 foo_1                                
-  Update on foo1_2 foo_2                                
-  ->  Append                                            
-        Subplans Removed: 1                             
-        ->  Seq Scan on foo1_1 foo_1                    
-              Filter: ((a = one()) OR (a = two()))      
-        ->  Seq Scan on foo1_2 foo_2                    
-              Filter: ((a = one()) OR (a = two()))      
-(37 rows)
-
-
-starting permutation: s1prep4 s2lock s1exec4 s2dropi s2unlock
-step s1prep4: SET plan_cache_mode = force_generic_plan;
-         PREPARE q4 AS SELECT * FROM generate_series(1, 1) WHERE EXISTS (SELECT * FROM foov WHERE a = $1 FOR UPDATE);
-         EXPLAIN (COSTS OFF) EXECUTE q4 (1);
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                                                   
--------------------------------------------------------------
-Result                                                       
-  One-Time Filter: (InitPlan 1).col1                         
-  InitPlan 1                                                 
-    ->  LockRows                                             
-          ->  Append                                         
-                Subplans Removed: 2                          
-                ->  Index Scan using foo1_1_a on foo1_1 foo_1
-                      Index Cond: (a = $1)                   
-  ->  Function Scan on generate_series                       
-(9 rows)
-
-step s2lock: SELECT pg_advisory_lock(12345);
-pg_advisory_lock
-----------------
-                
-(1 row)
-
-step s1exec4: LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q4 (1); <waiting ...>
-step s2dropi: DROP INDEX foo1_1_a;
-step s2unlock: SELECT pg_advisory_unlock(12345);
-pg_advisory_unlock
-------------------
-t                 
-(1 row)
-
-step s1exec4: <... completed>
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is not valid
-s1: NOTICE:  Finished ExecutorStart(): CachedPlan is valid
-QUERY PLAN                                  
---------------------------------------------
-Result                                      
-  One-Time Filter: (InitPlan 1).col1        
-  InitPlan 1                                
-    ->  LockRows                            
-          ->  Append                        
-                Subplans Removed: 2         
-                ->  Seq Scan on foo1_1 foo_1
-                      Filter: (a = $1)      
-  ->  Function Scan on generate_series      
-(9 rows)
-
index 58159bfc5747397d1b4987440c64b3044d43bae9..b53488f76d2adf6f10c66b6a9d4365650e76a408 100644 (file)
@@ -24,7 +24,6 @@ tests += {
     'specs': [
       'partition-addition',
       'partition-removal-1',
-      'cached-plan-inval',
     ],
   },
 }
diff --git a/src/test/modules/delay_execution/specs/cached-plan-inval.spec b/src/test/modules/delay_execution/specs/cached-plan-inval.spec
deleted file mode 100644 (file)
index f0cf06f..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-# Test to check that invalidation of cached generic plans during ExecutorStart
-# is correctly detected causing an updated plan to be re-executed.
-
-setup
-{
-  CREATE TABLE foo (a int, b text) PARTITION BY RANGE (a);
-  CREATE TABLE foo1 PARTITION OF foo FOR VALUES FROM (MINVALUE) TO (3) PARTITION BY RANGE (a);
-  CREATE TABLE foo1_1 PARTITION OF foo1 FOR VALUES FROM (MINVALUE) TO (2);
-  CREATE TABLE foo1_2 PARTITION OF foo1 FOR VALUES FROM (2) TO (3);
-  CREATE INDEX foo1_1_a ON foo1_1 (a);
-  CREATE TABLE foo2 PARTITION OF foo FOR VALUES FROM (3) TO (MAXVALUE);
-  INSERT INTO foo SELECT generate_series(-1000, 1000);
-  CREATE VIEW foov AS SELECT * FROM foo;
-  CREATE FUNCTION one () RETURNS int AS $$ BEGIN RETURN 1; END; $$ LANGUAGE PLPGSQL STABLE;
-  CREATE FUNCTION two () RETURNS int AS $$ BEGIN RETURN 2; END; $$ LANGUAGE PLPGSQL STABLE;
-  CREATE TABLE bar (a int, b text) PARTITION BY LIST(a);
-  CREATE TABLE bar1 PARTITION OF bar FOR VALUES IN (1);
-  CREATE INDEX ON bar1(a);
-  CREATE TABLE bar2 PARTITION OF bar FOR VALUES IN (2);
-  CREATE RULE update_foo AS ON UPDATE TO foo DO ALSO UPDATE bar SET a = a WHERE a = one();
-  CREATE RULE update_bar AS ON UPDATE TO bar DO ALSO SELECT 1;
-  ANALYZE;
-}
-
-teardown
-{
-  DROP VIEW foov;
-  DROP RULE update_foo ON foo;
-  DROP TABLE foo, bar;
-  DROP FUNCTION one(), two();
-}
-
-session "s1"
-step "s1prep"   { SET plan_cache_mode = force_generic_plan;
-         PREPARE q AS SELECT * FROM foov WHERE a = $1 FOR UPDATE;
-         EXPLAIN (COSTS OFF) EXECUTE q (1); }
-
-step "s1prep2"   { SET plan_cache_mode = force_generic_plan;
-         PREPARE q2 AS SELECT * FROM foov WHERE a = one() or a = two();
-         EXPLAIN (COSTS OFF) EXECUTE q2; }
-
-step "s1prep3"   { SET plan_cache_mode = force_generic_plan;
-         PREPARE q3 AS UPDATE foov SET a = a WHERE a = one() or a = two();
-         EXPLAIN (COSTS OFF) EXECUTE q3; }
-
-step "s1prep4"   { SET plan_cache_mode = force_generic_plan;
-         PREPARE q4 AS SELECT * FROM generate_series(1, 1) WHERE EXISTS (SELECT * FROM foov WHERE a = $1 FOR UPDATE);
-         EXPLAIN (COSTS OFF) EXECUTE q4 (1); }
-
-step "s1exec"  { LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q (1); }
-step "s1exec2" { LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q2; }
-step "s1exec3" { LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q3; }
-step "s1exec4" { LOAD 'delay_execution';
-         SET delay_execution.executor_start_lock_id = 12345;
-         EXPLAIN (COSTS OFF) EXECUTE q4 (1); }
-
-session "s2"
-step "s2lock"  { SELECT pg_advisory_lock(12345); }
-step "s2unlock"    { SELECT pg_advisory_unlock(12345); }
-step "s2dropi" { DROP INDEX foo1_1_a; }
-
-# In all permutations below, while "s1exec", "s1exec2", etc. wait to
-# acquire the advisory lock, "s2drop" drops the index being used in the
-# cached plan. When "s1exec" and others are unblocked and begin initializing
-# the plan, including acquiring necessary locks on partitions, the concurrent
-# index drop is detected. This causes plan initialization to be aborted,
-# prompting the caller to retry with a new plan.
-
-# Case with runtime pruning using EXTERN parameter
-permutation "s1prep" "s2lock" "s1exec" "s2dropi" "s2unlock"
-
-# Case with runtime pruning using stable function
-permutation "s1prep2" "s2lock" "s1exec2" "s2dropi" "s2unlock"
-
-# Case with a rule adding another query causing the CachedPlan to contain
-# multiple PlannedStmts
-permutation "s1prep3" "s2lock" "s1exec3" "s2dropi" "s2unlock"
-
-# Case with run-time pruning inside a subquery
-permutation "s1prep4" "s2lock" "s1exec4" "s2dropi" "s2unlock"