Concurrent DML (Data Manipulation Language) operations running on a Greenplum Heap Table may encounter a "deadlock detected" error, particularly when the Global Deadlock Detector (GDD) is disabled/default (gp_enable_global_deadlock_detector = off) .
This type of deadlock is a local deadlock happened on the coordinator, originating from conflicting lock mode requirements within the same segment.
Example Error Log Snippet:
2025-10-21 00:00:01.780553 CST,"<username>","<dbname>",p1408928,th1537012864,"xx.xx.xx.xx","60664",2025-10-20 23:59:55 CST,0,con13821124,cmd50,seg-1,,dx165631782,,sx1,"ERROR","40P01","deadlock detected","Process 1408928 waits for ExclusiveLock on relation <relation_oid> of database <database_oid>; blocked by process 1408957.
GPDB 6X
Such symptom is observed with the following SQL pattern, in short, doing a INSERT and then UPDATE/DELETE in a transaction block.
begin;
insert into t values (1);
update t set b = b + 1;
end;
The root cause is the Greenplum implementation of lock mode acquisition for UPDATE statements when GDD is disabled, leading to a lock-mode upgrade pattern that causes a cycle of mutual waiting.
The lock acquired by an UPDATE statement on the target relation (rel) differs based on the GDD state. An INSERT statement always acquires a non-conflicting RowExclusiveLock until the transaction ends.
| GDD State | Target Table Type | UPDATE/DELETE Statement Lock Mode |
GDD Disabled (off) |
Any | ExclusiveLock |
GDD Enabled (on) |
Heap Table | RowExclusiveLock |
GDD Enabled (on) |
AO Table (Append Optimized) | ExclusiveLock |
Consider a transaction block with two DMLs: INSERT followed by UPDATE. When GDD is disabled, the locking pattern is problematic: the transaction first acquires the less restrictive RowExclusiveLock for the INSERT, and then attempts to acquire the more restrictive ExclusiveLock for the UPDATE (a lock-mode upgrade)
Two concurrent transactions (A and B) running these SQLs will cause a local deadlock:
| Time | Transaction A Action | Transaction B Action | Result |
| t1 | Executes INSERT, holds RowExclusiveLock. |
Executes INSERT, holds RowExclusiveLock. |
Success: RowExclusiveLock does not conflict. |
| t2 | Tries UPDATE (Requires ExclusiveLock). |
(Continuing) | A Blocked: A is blocked by B's held RowExclusiveLock. A hangs. |
| t3 | (Continuing to hang) | Tries UPDATE (Requires ExclusiveLock). |
B Blocked: B is blocked by A's held RowExclusiveLock. B hangs. |
Result: A and B wait for each other, leading to a local deadlock detected by the Segment's local lock manager. If GDD were enabled, the UPDATE would also use RowExclusiveLock, preventing the conflicting upgrade attempt.
In this particular case:
Action: Set gp_enable_global_deadlock_detector to on.
Effect: This allows concurrent updates to run using the non-conflicting RowExclusiveLock on Heap Tables,preventing the lock-mode upgrade issue, and the lock during execution logic (ExecUpdate, ExecDelete) happens on segments. The GDD will then actively monitor for any complex global deadlocks that may arise.
More general suggestion:
To avoid deadlocks, make sure all transactions acquire locks on the same objects in the same order, and if multiple lock modes are involved for a single object, then transactions should always acquire the most restrictive mode first.
for example, doing UPDATE and then INSERT will not result in deadlock even when GDD set to off.
begin;
update t set b = b + 1;
insert into t values (1);
end;
Unlike standard Postgres, where INSERT, UPDATE, and DELETE all acquire the non-conflicting RowExclusiveLock, Greenplum (GPDB) sometimes requires a higher-level ExclusiveLock for UPDATE/DELETE.
This design choice is a safety measure against Global Deadlocks when the Global Deadlock Detector (GDD) is disabled: