TRUNCATE, concurrence avancée

Tronquer en série

      Suite de l’article illustrant un cas de concurrence entre un TRUNCATE et un SELECT. Il va s’agir ici de comprendre ce que signifie le fameux avertissement de la documentation officielle de PostgreSQL qui explique que TRUNCATE n’est pas sûre au niveau MVCC (multiversion concurrency control).
      TRUNCATE n’est pas sûre si vous essayez, dans une transaction en mode REPEATABLE READ ou SERIALIZABLE, de lire une table tronquée aprés le démarrage de votre transaction. Pour que le problème survienne il faut que vous n’ayez fait aucun accès sur cette table depuis le début de la transaction. Sinon, vous aurez en fait posé un verrou access share sur la table et le TRUNCATE attendra, ce qui n’est pas un problème au sens du respect des niveaux d’isolation.
      Illustration avec PostgreSQL 9.6 :

SELECT version(); version ------------------------------------------------------------------------------------------ PostgreSQL 9.6.0 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit (1 ligne) CREATE TABLE t1(c1 INTEGER); CREATE TABLE INSERT INTO t1(c1) VALUES(1); INSERT 0 1 COMMIT; COMMIT -- Dans toutes les sessions AUTOCOMMIT à off -- SESSION 1 START TRANSACTION; START TRANSACTION SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; SET -- un 1er select pour démarrer effectivement la transaction, en principe nous devons avoir la vision de la base telle qu’elle éait à ce timestamp SELECT CURRENT_TIMESTAMP; now ------------------------------- 2016-10-23 17:21:05.277637+02 (1 ligne) -- SESSION 2 TRUNCATE TABLE t1; TRUNCATE TABLE COMMIT; COMMIT -- SESSION 1 SELECT * FROM t1; c1 ---- (0 ligne) -- SESSION 0 INSERT INTO t1(c1) VALUES(1); INSERT 0 1 COMMIT; COMMIT -- SESSION 1 START TRANSACTION; START TRANSACTION SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; SET -- un 1er select pour démarrer effectivement la transaction, en principe nous devons avoir la vision de la base telle qu’elle éait à ce timestamp SELECT CURRENT_TIMESTAMP; now ------------------------------- 2016-10-23 17:40:52.183946+02 (1 ligne) -- SESSION 2 DELETE FROM t1; DELETE 1 COMMIT; COMMIT -- SESSION 1 SELECT * FROM t1; c1 ---- 1 (1 ligne) -- SESSION 2 TRUNCATE TABLE t1; -- en attente puisque la session 1 a accédé à t1 !

      OK normalement le 1er "SELECT * FROM t1" aurait dû voir la ligne supprimée par le TRUNCATE de l’autre session. Si vous effectuez le test avec un DELETE au lieu du TRUNCATE c’est le cas. Mais c’est vraiment un problème très mineur. Dans 99,999..% des utilisations, TRUNCATE ne pose aucun problème dans le respect des niveaux d’isolation sous PostgreSQL.
      Question qui pourrait se poser après ces tests...quid de pg_dump si des TRUNCATE surviennent en cours d’export ? Et bien rassurez-vous. Il n’y a pas de problème de cohérence de l’export. En revanche, les TRUNCATE seront bloqués car un verrou de type access share est posé sur tous les tables concernées par l’export.

Mise à jour : 23/10/2016