Analizzando alcuni log SQL, ho notato che alcune query non sono coerenti con quanto previsto dal codice a causa dell’uso misto di Active Record e mini_sql.
Quando si tenta di aggiornare alcuni reviewable, si incrementa la versione all’interno di una transazione per prevenire possibili condizioni di gara (come mostra il frammento di codice sottostante). Sebbene sembri che l’incremento faccia parte della transazione, in realtà non lo è.
Reviewable.transaction è una funzionalità di Active Record, mentre increment_version! utilizza mini_sql:
Active Record non è a conoscenza di ciò che fa mini_sql. Sembra che Active Record utilizzi una funzionalità lazy, in cui BEGIN viene emesso subito prima della prima query all’interno della transazione. Quindi la versione viene incrementata per prima e successivamente la transazione inizia immediatamente prima che venga emessa la prima query da Active Record. Diventa qualcosa del genere:
UPDATE reviewables SET version = version + 1 WHERE version=version AND id = reviewable.id RETURNING version;
BEGIN;
...
Con l’incremento della versione esterno alla transazione, alcune garanzie fornite dal locking ottimistico non vengono ottenute. Perché l’incremento non è atomico rispetto alla transazione.