Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-Pick Lock leaf partitions for Insert Statement when GDD disabled #971

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/backend/executor/execMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
break;
}
}

SIMPLE_FAULT_INJECTOR("func_init_plan_end");
}

/*
Expand Down
28 changes: 27 additions & 1 deletion src/backend/parser/parse_clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "catalog/pg_amproc.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "miscadmin.h"
Expand Down Expand Up @@ -264,6 +265,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
ParseCallbackState pcbstate;
ParseNamespaceItem *nsitem;
bool lockUpgraded = false;
LOCKMODE lockmode;

/*
* ENRs hide tables of the same name, so we need to check for them first.
Expand Down Expand Up @@ -310,11 +312,35 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
pstate->p_target_relation = parserOpenTable(pstate, relation, RowExclusiveLock, &lockUpgraded);
}

lockmode = lockUpgraded ? ExclusiveLock : RowExclusiveLock;

if (Gp_role == GP_ROLE_DISPATCH &&
pstate->p_is_insert &&
!gp_enable_global_deadlock_detector &&
pstate->p_target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
/*
* Greenplum specific code:
* When GDD is disabled, and we are inserting into a partition table,
* then we need to lock all leaf partitions. The reason is:
* 1. we cannot predict which leaf partitions will be inserted
* 2. if we do not hold lock on leaf partitions on QD, then this
* insert statement will be dispatched to QEs, and not dispatch
* is async, so on some segments, this session's insert will
* first hold locks on leaf partition and block others; however,
* on some segments, this session's insert will be blocked by
* others. Thus we have risk to have global deadlock.
* See issue https://github.com/greenplum-db/gpdb/issues/13652 for details.
*/
(void) find_all_inheritors(RelationGetRelid(pstate->p_target_relation),
lockmode, NULL);
}

/*
* Now build an RTE and a ParseNamespaceItem.
*/
nsitem = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
lockUpgraded ? ExclusiveLock : RowExclusiveLock, /* CDB */
lockmode, /* CDB */
relation->alias, inh, false);

/* remember the RTE/nsitem as being the query target */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-- Insert statement on root partition, if GDD is enabled, QD will not lock leaf
-- parititions for better concurrency performance. So when the insert statement
-- is dispatched to segments async, some of statement will lock leaf partition
-- and execute, some might be blocked by other sessions and lead to global
-- deadlock. This test file is in GDD suites, it verify that such deadlock can
-- be broken by GDD.
-- See Issue https://github.com/greenplum-db/gpdb/issues/13652 for details.

-- NOTE: this test case is better to run both with GDD and withoug GDD.
-- with GDD it is running within gdd test suites to test GDD can break the deadlock;
-- without GDD it is running to show that no deadlock happens.

create table rank_13652 (id int, year int) partition by range (year) (start (2006) end (2009) every (1));
CREATE

1: select gp_inject_fault('func_init_plan_end', 'suspend', dbid, current_setting('gp_session_id')::int) from gp_segment_configuration where content = 0 and role = 'p';
gp_inject_fault
-----------------
Success:
(1 row)

1&: insert into rank_13652 select i,i%3+2006 from generate_series(1, 30)i; <waiting ...>
select gp_wait_until_triggered_fault('func_init_plan_end', 1, dbid) from gp_segment_configuration where content = 0 and role = 'p';
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)

2&: truncate rank_13652_1_prt_2; <waiting ...>

select gp_inject_fault('func_init_plan_end', 'reset', dbid) from gp_segment_configuration where content = 0 and role = 'p';
gp_inject_fault
-----------------
Success:
(1 row)

1<: <... completed>
INSERT 30
2<: <... completed>
ERROR: canceling statement due to user request: "cancelled by global deadlock detector"

1q: ... <quitting>
2q: ... <quitting>

drop table rank_13652;
DROP
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-- Insert statement on root partition, if GDD is enabled, QD will not lock leaf
-- parititions for better concurrency performance. So when the insert statement
-- is dispatched to segments async, some of statement will lock leaf partition
-- and execute, some might be blocked by other sessions and lead to global
-- deadlock. This test file is in GDD suites, it verify that such deadlock can
-- be broken by GDD.
-- See Issue https://github.com/greenplum-db/gpdb/issues/13652 for details.

-- NOTE: this test case is better to run both with GDD and withoug GDD.
-- with GDD it is running within gdd test suites to test GDD can break the deadlock;
-- without GDD it is running to show that no deadlock happens.

create table rank_13652 (id int, year int) partition by range (year) (start (2006) end (2009) every (1));
CREATE

1: select gp_inject_fault('func_init_plan_end', 'suspend', dbid, current_setting('gp_session_id')::int) from gp_segment_configuration where content = 0 and role = 'p';
gp_inject_fault
-----------------
Success:
(1 row)

1&: insert into rank_13652 select i,i%3+2006 from generate_series(1, 30)i; <waiting ...>
select gp_wait_until_triggered_fault('func_init_plan_end', 1, dbid) from gp_segment_configuration where content = 0 and role = 'p';
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)

2&: truncate rank_13652_1_prt_2; <waiting ...>

select gp_inject_fault('func_init_plan_end', 'reset', dbid) from gp_segment_configuration where content = 0 and role = 'p';
gp_inject_fault
-----------------
Success:
(1 row)

1<: <... completed>
INSERT 30
2<: <... completed>
TRUNCATE

1q: ... <quitting>
2q: ... <quitting>

drop table rank_13652;
DROP
122 changes: 75 additions & 47 deletions src/test/isolation2/expected/lockmodes.out
Original file line number Diff line number Diff line change
Expand Up @@ -1024,28 +1024,42 @@ ABORT
-- For detailed behavior and notes, please refer below
-- cases's comments.
-- Details: https://groups.google.com/a/greenplum.org/g/gpdb-dev/c/wAPKpJzhbpM
-- start_ignore
1:DROP TABLE IF EXISTS t_lockmods_part_tbl_upd_del;
-- Issue: https://github.com/greenplum-db/gpdb/issues/13652
1:DROP TABLE IF EXISTS t_lockmods_part_tbl_dml;
DROP
-- end_ignore

1:CREATE TABLE t_lockmods_part_tbl_upd_del (a int, b int, c int) PARTITION BY RANGE(b) (START(1) END(3) EVERY(1));
1:CREATE TABLE t_lockmods_part_tbl_dml (a int, b int, c int) PARTITION BY RANGE(b) (START(1) END(3) EVERY(1));
CREATE
1:INSERT INTO t_lockmods_part_tbl_upd_del SELECT i, 1, i FROM generate_series(1,10)i;
1:INSERT INTO t_lockmods_part_tbl_dml SELECT i, 1, i FROM generate_series(1,10)i;
INSERT 10

--
1: BEGIN;
BEGIN
1: DELETE FROM t_lockmods_part_tbl_upd_del;
1: DELETE FROM t_lockmods_part_tbl_dml;
DELETE 10
-- on QD, there's a lock on the root and the target partition
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+---------------+---------+-------------------------------------
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_2
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del
locktype | mode | granted | relation
----------+---------------+---------+---------------------------------
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_2
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml
(3 rows)
1: ROLLBACK;
ROLLBACK

1: BEGIN;
BEGIN
1: INSERT INTO t_lockmods_part_tbl_dml SELECT i, 1, i FROM generate_series(1,10)i;
INSERT 10
-- without GDD, it will lock all leaf partitions on QD
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+------------------+---------+---------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_2
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml
(3 rows)
1: ROLLBACK;
ROLLBACK
Expand Down Expand Up @@ -1075,44 +1089,44 @@ ROLLBACK

1: BEGIN;
BEGIN
1: UPDATE t_lockmods_part_tbl_upd_del SET c = 1 WHERE c = 1;
1: UPDATE t_lockmods_part_tbl_dml SET c = 1 WHERE c = 1;
UPDATE 1
-- on QD, there's a lock on the root and the target partition
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+---------------+---------+-------------------------------------
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_2
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del
locktype | mode | granted | relation
----------+---------------+---------+---------------------------------
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_2
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml
(3 rows)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

1: BEGIN;
BEGIN
1: DELETE FROM t_lockmods_part_tbl_upd_del_1_prt_1;
1: DELETE FROM t_lockmods_part_tbl_dml_1_prt_1;
DELETE 10
-- since the delete operation is on leaf part, there will be a lock on QD
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+---------------+---------+-------------------------------------
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
locktype | mode | granted | relation
----------+---------------+---------+---------------------------------
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
(1 row)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

1: BEGIN;
BEGIN
1: UPDATE t_lockmods_part_tbl_upd_del_1_prt_1 SET c = 1 WHERE c = 1;
1: UPDATE t_lockmods_part_tbl_dml_1_prt_1 SET c = 1 WHERE c = 1;
UPDATE 1
-- since the update operation is on leaf part, there will be a lock on QD
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+-----------------+---------+-------------------------------------
relation | AccessShareLock | t | t_lockmods_part_tbl_upd_del
relation | ExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
locktype | mode | granted | relation
----------+-----------------+---------+---------------------------------
relation | AccessShareLock | t | t_lockmods_part_tbl_dml
relation | ExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
(2 rows)
1: ROLLBACK;
ROLLBACK
Expand Down Expand Up @@ -2123,65 +2137,79 @@ ABORT

1: BEGIN;
BEGIN
1: DELETE FROM t_lockmods_part_tbl_upd_del;
1: DELETE FROM t_lockmods_part_tbl_dml;
DELETE 10
-- on QD, there's a lock on the root and the target partition
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+------------------+---------+-------------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_2
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del
locktype | mode | granted | relation
----------+------------------+---------+---------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_2
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml
(3 rows)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

1: BEGIN;
BEGIN
1: UPDATE t_lockmods_part_tbl_upd_del SET c = 1 WHERE c = 1;
1: UPDATE t_lockmods_part_tbl_dml SET c = 1 WHERE c = 1;
UPDATE 1
-- on QD, there's a lock on the root and the target partition
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+------------------+---------+-------------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_2
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del
locktype | mode | granted | relation
----------+------------------+---------+---------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_2
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml
(3 rows)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

1: BEGIN;
BEGIN
1: DELETE FROM t_lockmods_part_tbl_upd_del_1_prt_1;
1: DELETE FROM t_lockmods_part_tbl_dml_1_prt_1;
DELETE 10
-- since the delete operation is on leaf part, there will be a lock on QD
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+------------------+---------+-------------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
locktype | mode | granted | relation
----------+------------------+---------+---------------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
(1 row)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

1: BEGIN;
BEGIN
1: UPDATE t_lockmods_part_tbl_upd_del_1_prt_1 SET c = 1 WHERE c = 1;
1: UPDATE t_lockmods_part_tbl_dml_1_prt_1 SET c = 1 WHERE c = 1;
UPDATE 1
-- since the update operation is on leaf part, there will be a lock on QD
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+------------------+---------+-------------------------------------
relation | AccessShareLock | t | t_lockmods_part_tbl_upd_del
relation | RowExclusiveLock | t | t_lockmods_part_tbl_upd_del_1_prt_1
locktype | mode | granted | relation
----------+------------------+---------+---------------------------------
relation | AccessShareLock | t | t_lockmods_part_tbl_dml
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml_1_prt_1
(2 rows)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

1: BEGIN;
BEGIN
1: INSERT INTO t_lockmods_part_tbl_dml SELECT i, 1, i FROM generate_series(1,10)i;
INSERT 10
-- With GDD enabled, QD will only hold lock on root for insert
1: select * from show_locks_lockmodes;
locktype | mode | granted | relation
----------+------------------+---------+-------------------------
relation | RowExclusiveLock | t | t_lockmods_part_tbl_dml
(1 row)
1: ROLLBACK;
ROLLBACK
1q: ... <quitting>

-- 2.8 Verify behaviors of select with locking clause (i.e. select for update)
-- when running concurrently with index creation, for Heap tables.
-- For AO/CO tables, refer to create_index_allows_readonly.source.
Expand Down
Loading
Loading