Description
Symfony version(s) affected
6.4.6 but I guess it happens with newest ones too as there are no changes according to the changelog
Description
MySQL reports deadlocks like this one when using the Lock component:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-08-02 02:42:39 125380545455680
*** (1) TRANSACTION:
TRANSACTION 2755326397, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s)
MySQL thread id 610443, OS thread handle 125380125038144, query id 23603665462 10.10.13.8 app-read-write update
INSERT INTO lock_keys (key_id, key_token, key_expiration) VALUES ('c0c39cdb26e264f91ea3628f2ab2c87b04cee54fa6dc5bc2e75a499d022110a9', 'iyNOnmWrqJBjkAh5C77LMZjFKFDBQIBEntYf+RmzDVY=', UNIX_TIMESTAMP() + 300)
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 191 page no 180 n bits 192 index PRIMARY of table `bbwzm3n4u`.`lock_keys` trx id 2755326397 lock mode S locks rec but not gap
Record lock, heap no 63 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
0: len 30; hex 633063333963646232366532363466393165613336323866326162326338; asc c0c39cdb26e264f91ea3628f2ab2c8; (total 64 bytes);
1: len 6; hex 0000a43af1bc; asc : ;;
2: len 7; hex 01000054532ef0; asc TS. ;;
3: len 30; hex 77556d4f3542534134744e6c417766436445397a6c727863467a666b4c4d; asc wUmO5BSA4tNlAwfCdE9zlrxcFzfkLM; (total 44 bytes);
4: len 4; hex 66ac48cb; asc f H ;;
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 191 page no 180 n bits 192 index PRIMARY of table `bbwzm3n4u`.`lock_keys` trx id 2755326397 lock_mode X locks rec but not gap waiting
Record lock, heap no 63 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
0: len 30; hex 633063333963646232366532363466393165613336323866326162326338; asc c0c39cdb26e264f91ea3628f2ab2c8; (total 64 bytes);
1: len 6; hex 0000a43af1bc; asc : ;;
2: len 7; hex 01000054532ef0; asc TS. ;;
3: len 30; hex 77556d4f3542534134744e6c417766436445397a6c727863467a666b4c4d; asc wUmO5BSA4tNlAwfCdE9zlrxcFzfkLM; (total 44 bytes);
4: len 4; hex 66ac48cb; asc f H ;;
*** (2) TRANSACTION:
TRANSACTION 2755326398, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s)
MySQL thread id 624364, OS thread handle 125379871249984, query id 23603665471 10.10.13.10 app-read-write update
INSERT INTO lock_keys (key_id, key_token, key_expiration) VALUES ('c0c39cdb26e264f91ea3628f2ab2c87b04cee54fa6dc5bc2e75a499d022110a9', 'bT4yW/bpDThzAgHWxqjc0IEzJA7oCofHoKXqrTac808=', UNIX_TIMESTAMP() + 300)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 191 page no 180 n bits 192 index PRIMARY of table `bbwzm3n4u`.`lock_keys` trx id 2755326398 lock mode S locks rec but not gap
Record lock, heap no 63 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
0: len 30; hex 633063333963646232366532363466393165613336323866326162326338; asc c0c39cdb26e264f91ea3628f2ab2c8; (total 64 bytes);
1: len 6; hex 0000a43af1bc; asc : ;;
2: len 7; hex 01000054532ef0; asc TS. ;;
3: len 30; hex 77556d4f3542534134744e6c417766436445397a6c727863467a666b4c4d; asc wUmO5BSA4tNlAwfCdE9zlrxcFzfkLM; (total 44 bytes);
4: len 4; hex 66ac48cb; asc f H ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 191 page no 180 n bits 192 index PRIMARY of table `bbwzm3n4u`.`lock_keys` trx id 2755326398 lock_mode X locks rec but not gap waiting
Record lock, heap no 63 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
0: len 30; hex 633063333963646232366532363466393165613336323866326162326338; asc c0c39cdb26e264f91ea3628f2ab2c8; (total 64 bytes);
1: len 6; hex 0000a43af1bc; asc : ;;
2: len 7; hex 01000054532ef0; asc TS. ;;
3: len 30; hex 77556d4f3542534134744e6c417766436445397a6c727863467a666b4c4d; asc wUmO5BSA4tNlAwfCdE9zlrxcFzfkLM; (total 44 bytes);
4: len 4; hex 66ac48cb; asc f H ;;
*** WE ROLL BACK TRANSACTION (2)
How to reproduce
I don't know how to reproduce it because I don't know which key my application is trying to lock.
My understanding is that, somehow, the lock component first MySQL-wise locks the record with a SELECT
statement before the INSERT
statement.
That shared lock would come from a call to the PersistingStoreInterface::exists()
method from a call to the Lock::isAcquired()
method.
Possible Solution
This bug is about investigating what lock usage is causing this in my application.
Right now, it is difficult because lock_id
obfuscates the locked key:
It comes from \Symfony\Component\Lock\Store\DatabaseTableTrait::getHashedKey()
that hashes the key.
I understand this hash is useful when the key is longer than 64 chars.
An alternative may be
private function getHashedKey(Key $key): string
{
$keyAsString = (string) $key;
if (64 <= strlen($keyAsString)) {
return $keyAsString;
}
return hash('sha256', $keyAsString);
}
Additional Context
No response