I've ran into a need to figure this out on 9.6, but information on anything 9.6 or later would be appreciated.
I'm having an issue with my application being blocked on a database call because it's attempting to acquire an implicit transactionid
lock on a another running transaction. What I don't understand is why.
I understand that every transaction, when it starts, acquires an ExclusiveLock
on its own transaction ID (pg_locks). That's fine. That same page says that, "normally", only transactions that "change the state of the database" are assigned a permanent ID. I see the permanent ID in the locks table, so I assume that happened. However, the description is less clear, as the page neither specifies what does "normally" mean, nor what is considered "change of database state".
But then I can't find any information that specifies when would a statement attempt to acquire a SharedLock
on some other transaction. The only statement from pg_locks is:
When a process finds it necessary to wait specifically for another transaction to end, it does so by attempting to acquire share lock on the other transaction's ID (either virtual or permanent ID depending on the situation)
Which is really vague. There is no way to request a transaction lock (at least I don't see them listed in Explicit Locking)
So I'm looking for the following answers:
- When does Postgres decide to acquire (shared)
transactionid
lock on another transaction ID? - What exactly causes Postgres to assign a "permanent ID" to a transaction (that's less important for me to figure out how to fix my database use, but I was just generally confused by lack of hard information on this anywhere)
- What is making the insert query acquire
transactionid
lock in this particular case (also less important, because if I had the reference, I could have determined that myself)
Now, for the particular reason my query is blocked in this case:
Relevant pg_locks
content:
x=# select locktype,transactionid,virtualtransaction,pid,mode,granted
x-# from pg_locks where transactionid = '33682979' ;
locktype | transactionid | virtualtransaction | pid | mode | granted
---------------+---------------+--------------------+--------+---------------+---------
transactionid | 33682979 | 7/27909 | 476513 | ShareLock | f
transactionid | 33682979 | 5/387791 | 476509 | ExclusiveLock | t
(2 rows)
PID 476513
is stuck trying to insert:
x=# SELECT wait_event_type, state, query
x-# FROM pg_stat_activity
x-# WHERE pid = 476513;
wait_event_type | state | query
-----------------+--------+--------------------------------------------------------------------
Lock | active | INSERT INTO association (id, device, campaign) VALUES ($1, $2, $3)
(1 row)
I have full statement logging enabled, so I can also see what did PID 476509
do since declaring the last transaction. The only query that I can imagine has anything to do with it is the fact that it has deleted from association
table.
$ grep '476509.*execute' tx-lock.txt
<2020-06-17 13:58:37.743 CEST 476509.5/387791> LOG: execute S_13: BEGIN
<2020-06-17 13:58:37.743 CEST 476509.5/387791> LOG: execute <unnamed>: SELECT t0.* FROM campaign t0 WHERE t0.id = $1 FOR UPDATE
<2020-06-17 13:58:37.744 CEST 476509.5/387791> LOG: execute <unnamed>: SELECT t0.* FROM campaign t0 WHERE t0.id = $1 FOR UPDATE
<2020-06-17 13:58:37.752 CEST 476509.5/387791> LOG: execute <unnamed>: SELECT t0.* FROM association t0 WHERE (t0.enabled = $1 AND $2 = t0.campaign AND t0.statusCreated <> $3) LIMIT $4
<2020-06-17 13:58:37.759 CEST 476509.5/387791> LOG: execute <unnamed>: DELETE FROM association WHERE id IN (SELECT DISTINCT t0.id FROM association t0 WHERE (t0.campaign = $1))
<2020-06-17 13:58:37.796 CEST 476509.5/387791> LOG: execute <unnamed>: UPDATE campaign SET statusCreated = $1 WHERE id = $2
<2020-06-17 13:58:37.796 CEST 476509.5/387791> LOG: execute S_42: SELECT t0.id FROM lock t0 WHERE t0.id = $1
<2020-06-17 13:58:37.796 CEST 476509.5/387791> LOG: execute S_31: select id from lock where id = $1 for update
<2020-06-17 13:58:37.798 CEST 476509.5/387791> LOG: execute <unnamed>: SELECT t0.*, t1.*id FROM groups t0 INNER JOIN devices t1 ON t0.device_id = t1.id AND t0.device_tenancy = t1.tenancy LEFT OUTER JOIN group_defs t2 ON t0.DEVICEGROUP_ID = t2.id WHERE ((t0.group_id = $1...) AND t1.tenancy = $36) ORDER BY t0.id ASC, t1.id ASC LIMIT $37
(pardon some of the queries, they are created by a JPA, and not by me :) )