Traqueur, problème de verrouillage
Un problème applicatif
Téléchargement du traqueur pour PostgreSQL 12, 13, 14, 15, 16, 17
Téléchargement du traqueur pour PostgreSQL 11
Téléchargement du traqueur pour PostgreSQL 10
Téléchargement du traqueur pour PostgreSQL 9.6
Téléchargement du traqueur pour PostgreSQL 9.5
Téléchargement du traqueur pour PostgreSQL 9.4
Téléchargement du traqueur pour PostgreSQL 9.3
Journal des changements
Signalement de bugs via le forum
Licence identique à celle de PostgreSQL, open source type BSD
Dossier des versions
Les verrous sont nécessaires sur un SGBDR et les blocages causés par le verrouillage (wait_event_type "Lock" dans pg_stat_activity) ne sont pas exactement des problèmes de performance. Cependant, un verrouillage inadapté provoquera pour les utilisateurs le même symptôme que les problèmes de performance classiques : le sablier.
Le verrouillage excessif se traite sur le long terme au niveau de la logique applicative. Mais, pour débloquer rapidement des utilisateurs ou un traitement batch, il est nécessaire de savoir analyser la situation, voire de tuer certaines sessions etc.
Simulation dune situation de blocage avec PostgreSQL 10 beta :
select version();
version
-----------------------------------------------------------------------------------------------------
PostgreSQL 10beta1 on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-19) 6.3.0 20170618, 64-bit
create table t1(c1 int);
CREATE TABLE
insert into t1 values(1),(2),(3);
INSERT 0 3
create table t2(c2 int);
CREATE TABLE
insert into t2 values(1),(2),(3);
INSERT 0 3
-- SESSION A
start transaction;
START TRANSACTION
select pg_backend_pid();
pg_backend_pid
----------------
803
update t1 set c1 = 11 where c1 = 1;
UPDATE 1
-- SESSION B
start transaction;
START TRANSACTION
select pg_backend_pid();
pg_backend_pid
----------------
843
update t1 set c1 = 22 where c1 = 2;
UPDATE 1
-- SESSION C
select pg_backend_pid();
pg_backend_pid
----------------
880
start transaction;
START TRANSACTION
update t1 set c1 = 33 where c1 = 3;
UPDATE 1
delete from t1;
-- EN ATTENTE
-- SESSION D
select pg_backend_pid();
pg_backend_pid
----------------
914
truncate table t1;
-- EN ATTENTE
-- SESSION E
select pg_backend_pid();
pg_backend_pid
----------------
944
select * from t1;
-- EN ATTENTE
-- SESSION F
select pg_backend_pid();
pg_backend_pid
----------------
973
start transaction;
START TRANSACTION
update t2 set c2 = 11 where c2 = 1;
UPDATE 1
-- SESSION F
select pg_backend_pid();
pg_backend_pid
----------------
1007
update t2 set c2 = 111 where c2 = 1;
-- EN ATTENTE
-- SESSION G
select pg_backend_pid();
pg_backend_pid
----------------
1038
update t2 set c2 = 22 where c2 = 2;
UPDATE 1
select pg_sleep(30000);
-- EN ATTENTE
-- SESSION H
select pg_backend_pid();
pg_backend_pid
----------------
1241
update t1 set c1 = 333 where c1 = 3;
-- EN ATTENTE
Le décor est planté, beaucoup dutilisateurs se plaignent. Première analyse avec une version ancienne du traqueur :
./traqueur_0_05_19.sh
traqueur 0.05.19beta - outil de diagnostic performance pour PostgreSQL 9.6, 10
INFORMATION, pas d'options de connexions indiquees, utilisation de la base traqueur detectee ...
INFORMATION, preparation de la collecte ...
INFORMATION, execution de la collecte et presentation des resultats ...
busy_pc | distinct_exe | db | pid | pg_user | client_ip | application | query | blocker
---------+--------------+----------+------+----------+-----------+-------------+--------------------------------------+---------
100 | 1 / 50 | postgres | 880 | postgres | | psql | delete from t1; | 803
100 | 1 / 50 | postgres | 914 | postgres | | psql | truncate table t1; | 803
100 | 1 / 50 | postgres | 914 | postgres | | psql | truncate table t1; | 843
100 | 1 / 50 | postgres | 914 | postgres | | psql | truncate table t1; | 880
100 | 1 / 50 | postgres | 944 | postgres | | psql | select * from t1; | 803
100 | 1 / 50 | postgres | 944 | postgres | | psql | select * from t1; | 843
100 | 1 / 50 | postgres | 944 | postgres | | psql | select * from t1; | 880
100 | 1 / 50 | postgres | 1007 | postgres | | psql | update t2 set c2 = 111 where c2 = 1; | 973
100 | 1 / 50 | postgres | 1038 | postgres | | psql | select pg_sleep(30000); |
100 | 1 / 50 | postgres | 1241 | postgres | | psql | update t1 set c1 = 333 where c1 = 3; | 803
100 | 1 / 50 | postgres | 1241 | postgres | | psql | update t1 set c1 = 333 where c1 = 3; | 843
100 | 1 / 50 | postgres | 1241 | postgres | | psql | update t1 set c1 = 333 where c1 = 3; | 880
(12 lignes)
Ouille ?!? Le vieux traqueur a du mal. Les résultats sont faussés car un même process peut apparaître plusieurs fois à 100% dactivité, bloqué par des process différents qui sont eux-mêmes parfois bloqués. Qui sont les assassins, les "final blockers", et qui sont les victimes ?
Avant dinterroger directement les tables du catalogue, tentons dutiliser le traqueur dans une version plus récente :
./traqueur.sh
traqueur 0.08.00beta - outil de diagnostic performance pour PostgreSQL 9.6, 10
INFORMATION, pas d'options de connexions indiquees, utilisation de la base traqueur detectee ...
INFORMATION, preparation de la collecte ...
INFORMATION, execution de la collecte et presentation des resultats ...
busy_pc | distinct_exe | db | pid | pg_user | client_ip | application | query | wait_event_type | blockers
---------+--------------+----------+------+----------+-----------+-------------+--------------------------------------+-----------------+-----------
100 | 1 / 50 | postgres | 880 | postgres | | psql | delete from t1; | Lock | {803}
100 | 1 / 50 | postgres | 914 | postgres | | psql | truncate table t1; | Lock | {803,843}
100 | 1 / 50 | postgres | 944 | postgres | | psql | select * from t1; | Lock | {803,843}
100 | 1 / 50 | postgres | 1007 | postgres | | psql | update t2 set c2 = 111 where c2 = 1; | Lock | {973}
100 | 1 / 50 | postgres | 1038 | postgres | | psql | select pg_sleep(30000); | Timeout |
100 | 1 / 50 | postgres | 1241 | postgres | | psql | update t1 set c1 = 333 where c1 = 3; | Lock | {803,843}
(6 lignes)
La situation est plus claire. Ici, par exemple, le "select * from t1" du process 944 peut être directement bloqué par le process 914 effectuant un "truncate table t1". Mais le process 914 étant lui-même au final bloqué par les process 803 et 843, cest cette liste {803,843} qui est donnée pour 944. Idem pour 880 et 1241 : ce sont toujours 803 et/ou 843 qui sont coupables sans être victimes.
Cette version du traqueur donne en effet pour chaque process bloqué par un verrou une liste de ses bloqueurs finaux, cest à dire une liste de process le bloquant directement ou indirectement et nattendant pas dobtenir des verrous. Il ne faut pas directement en conclure quils doivent être tués. Ces bloqueurs vont peut-être finir par libérer les verrous et sont peut-être en train de travailler, même sils napparaissent pas dans la liste des résultats du traqueur qui neffectue quun sondage.
Nous allons donc examiner plus attentivement la situation des process 803 et 843 avant de décider de leur sort. Ils ne semblent pas actifs et le traqueur ne va donc pas ici nous aider. Il faut directement interroger pg_stat_activity :
\x
select * from pg_stat_activity where pid in (803,843);
-[ RECORD 1 ]----+------------------------------------
datid | 12872
datname | postgres
pid | 843
usesysid | 10
usename | postgres
application_name | psql
client_addr |
client_hostname |
client_port | -1
backend_start | 2017-07-09 13:13:12.541736+02
xact_start | 2017-07-09 13:30:34.834221+02
query_start | 2017-07-09 13:30:36.754948+02
state_change | 2017-07-09 13:30:36.755307+02
wait_event_type | Client
wait_event | ClientRead
state | idle in transaction
backend_xid | 24839
backend_xmin |
query | update t1 set c1 = 22 where c1 = 2;
backend_type | client backend
-[ RECORD 2 ]----+------------------------------------
datid | 12872
datname | postgres
pid | 803
usesysid | 10
usename | postgres
application_name | psql
client_addr |
client_hostname |
client_port | -1
backend_start | 2017-07-09 13:07:13.222123+02
xact_start | 2017-07-09 13:29:33.012199+02
query_start | 2017-07-09 13:29:40.090936+02
state_change | 2017-07-09 13:29:40.091279+02
wait_event_type | Client
wait_event | ClientRead
state | idle in transaction
backend_xid | 24838
backend_xmin |
query | update t1 set c1 = 11 where c1 = 1;
backend_type | client backend
select current_timestamp;
-[ RECORD 1 ]-----+------------------------------
current_timestamp | 2017-07-09 13:54:25.518149+02
Bon, cela fait 25 minutes que 803 et 843 nont rien fait et personne ne peut reprendre la main sur ces sessions. Il va falloir agir. La décision est prise de les tuer. Attention, le travail de leurs transactions en cours sera annulé comme sils avaient lancé un ROLLBACK :
select pg_terminate_backend(803);
pg_terminate_backend
----------------------
t
./traqueur.sh -d 1
traqueur 0.08.00beta - outil de diagnostic performance pour PostgreSQL 9.6, 10
INFORMATION, pas d'options de connexions indiquees, utilisation de la base traqueur detectee ...
INFORMATION, preparation de la collecte ...
INFORMATION, execution de la collecte et presentation des resultats ...
busy_pc | distinct_exe | db | pid | pg_user | client_ip | application | query | wait_event_type | blockers
---------+--------------+----------+------+----------+-----------+-------------+--------------------------------------+-----------------+----------
100 | 1 / 10 | postgres | 880 | postgres | | psql | delete from t1; | Lock | {843}
100 | 1 / 10 | postgres | 914 | postgres | | psql | truncate table t1; | Lock | {843}
100 | 1 / 10 | postgres | 944 | postgres | | psql | select * from t1; | Lock | {843}
100 | 1 / 10 | postgres | 1007 | postgres | | psql | update t2 set c2 = 111 where c2 = 1; | Lock | {973}
100 | 1 / 10 | postgres | 1038 | postgres | | psql | select pg_sleep(30000); | Timeout |
100 | 1 / 10 | postgres | 1241 | postgres | | psql | update t1 set c1 = 333 where c1 = 3; | Lock | {843}
select pg_terminate_backend(843);
pg_terminate_backend
----------------------
t
./traqueur.sh -d 1
traqueur 0.08.00beta - outil de diagnostic performance pour PostgreSQL 9.6, 10
INFORMATION, pas d'options de connexions indiquees, utilisation de la base traqueur detectee ...
INFORMATION, preparation de la collecte ...
INFORMATION, execution de la collecte et presentation des resultats ...
busy_pc | distinct_exe | db | pid | pg_user | client_ip | application | query | wait_event_type | blockers
---------+--------------+----------+------+----------+-----------+-------------+--------------------------------------+-----------------+----------
100 | 1 / 10 | postgres | 914 | postgres | | psql | truncate table t1; | Lock | {880}
100 | 1 / 10 | postgres | 944 | postgres | | psql | select * from t1; | Lock | {880}
100 | 1 / 10 | postgres | 1007 | postgres | | psql | update t2 set c2 = 111 where c2 = 1; | Lock | {973}
100 | 1 / 10 | postgres | 1038 | postgres | | psql | select pg_sleep(30000); | Timeout |
100 | 1 / 10 | postgres | 1241 | postgres | | psql | update t1 set c1 = 333 where c1 = 3; | Lock | {880}
select * from pg_stat_activity where pid = 880;
-[ RECORD 1 ]----+------------------------------
datid | 12872
datname | postgres
pid | 880
usesysid | 10
usename | postgres
application_name | psql
client_addr |
client_hostname |
client_port | -1
backend_start | 2017-07-09 13:16:21.913152+02
xact_start | 2017-07-09 13:31:18.617386+02
query_start | 2017-07-09 13:32:21.314876+02
state_change | 2017-07-09 14:01:31.094092+02
wait_event_type | Client
wait_event | ClientRead
state | idle in transaction
backend_xid | 24840
backend_xmin |
query | delete from t1;
backend_type | client backend
-- SESSION C
delete from t1;
DELETE 3
rollback;
ROLLBACK
./traqueur.sh -d 1
traqueur 0.08.00beta - outil de diagnostic performance pour PostgreSQL 9.6, 10
INFORMATION, pas d'options de connexions indiquees, utilisation de la base traqueur detectee ...
INFORMATION, preparation de la collecte ...
INFORMATION, execution de la collecte et presentation des resultats ...
busy_pc | distinct_exe | db | pid | pg_user | client_ip | application | query | wait_event_type | blockers
---------+--------------+----------+------+----------+-----------+-------------+--------------------------------------+-----------------+----------
100 | 1 / 10 | postgres | 1007 | postgres | | psql | update t2 set c2 = 111 where c2 = 1; | Lock | {973}
100 | 1 / 10 | postgres | 1038 | postgres | | psql | select pg_sleep(30000); | Timeout |
Après la mort de 803 et 843, cest le delete lancé par 880 qui a causé un nouveau blocage. Cependant la session "C" du process 880 nétait pas orpheline, un ROLLBACK volontaire y a été effectué et a terminé de libérer les requêtes agissant sur t1. Reste à présent à soccuper de 973 qui bloque 1007 mais cest la même logique. 1038 semble bloqué mais il est en fait en train de dormir et ne bloque (pour linstant) personne.
Conclusion
Le traqueur evolue régulièrement et dispose depuis la 0.08.00 de capacités pour mieux analyser les problèmes de verrouillage. Nhésitez-pas à me poser tout question ou me signaler tout bug que vous pourriez rencontrer dans son utilisation.
Mise à jour : 09/07/2017