Sus opciones son:
-
Ejecutar en
SERIALIZABLE
aislamiento. Las transacciones interdependientes se anularán en el compromiso por tener una falla de serialización. Recibirá una gran cantidad de spam de registro de errores y hará muchos reintentos, pero funcionará de manera confiable. -
Defina un
UNIQUE
restricción y vuelva a intentarlo en caso de falla, como anotó. Los mismos problemas que arriba. -
Si hay un objeto principal, puede
SELECT ... FOR UPDATE
el objeto principal antes de hacer sumax
consulta. En este caso,SELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE
. Estás usandobar
como candado para todos losfoo
s con esebar_id
. Entonces puede saber que es seguro continuar, siempre que cada consulta que esté haciendo su incremento de contador lo haga de manera confiable. Esto puede funcionar bastante bien.Esto todavía hace una consulta agregada para cada llamada, lo cual (según la siguiente opción) es innecesario, pero al menos no envía spam al registro de errores como las opciones anteriores.
-
Usa una mesa de mostrador. Esto es lo que haría. Ya sea en
bar
, o en una mesa auxiliar comobar_foo_counter
, adquiera un ID de fila usandoUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counter
o la opción menos eficiente si su marco no puede manejar
RETURNING
:SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;
Luego, en la misma transacción , use la fila de contador generada para el
number
. Cuando confirmas, la fila de la tabla de contador para esebar_id
se desbloquea para la próxima consulta a utilizar. Si retrocede, el cambio se descarta.
Recomiendo el enfoque de contador, usando una mesa auxiliar dedicada para el contador en lugar de agregar una columna a bar
. Eso es más limpio de modelar y significa que creas menos actualizaciones en la bar
, que puede ralentizar las consultas a bar
.