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:
transactionid
lock on another transaction ID?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 :) )
PostgreSQL locks often called “write locks” or “exclusive locks,” restrict users from modifying a row or a PostgreSQL table's contents. Rows that have undergone a DELETE or UPDATE operation will be locked solely until the transaction is finished.
The event Lock:transactionid occurs when a transaction is trying to acquire a row-level lock that has already been granted to a transaction that is running at the same time. The session that shows the Lock:transactionid wait event is blocked because of this lock.
In PostgreSQL, when a transaction cannot acquire the requested lock within a certain amount of time (configured by `deadlock_timeout`, with default value of 1 second), it begins deadlock detection.
PostgreSQL advisory locks are application-level, cooperative locks explicitly locked and unlocked by the user's application code. An application can use PostgreSQL advisory locks to coordinate activity across multiple sessions.
That usually indicates that the transaction waits for a row lock that is held by the transaction it is waiting for.
Row locks are not stored in the shared memory locking table permanently, but on the table row itself in the xmax
system column. The value stored there is the transaction number of the blocking transaction (usually).
Once a transaction has found out who holds the lock on that row, it starts waiting for that transaction to finish, which will release the exclusive lock it holds on its own transaction ID.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With