Uw opties zijn:
-
Uitvoeren in
SERIALIZABLE
isolatie. Onderling afhankelijke transacties worden afgebroken bij commit omdat ze een serialisatiefout hebben. U krijgt veel spam in het foutenlogboek en u zult het vaak opnieuw proberen, maar het zal betrouwbaar werken. -
Definieer een
UNIQUE
beperking en probeer het opnieuw bij een mislukking, zoals u opmerkte. Dezelfde problemen als hierboven. -
Als er een bovenliggend object is, kunt u
SELECT ... FOR UPDATE
het bovenliggende object voordat u uwmax
. doet vraag. In dit geval zou uSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE
. Je gebruiktbar
als slot voor allefoo
s met diebar_id
. U kunt dan weten dat het veilig is om door te gaan, zolang elke query die uw teller doet dit op betrouwbare wijze doet. Dit kan best goed werken.Dit voert nog steeds een geaggregeerde query uit voor elke oproep, wat (per volgende optie) niet nodig is, maar het spamt in ieder geval het foutenlogboek niet zoals de bovenstaande opties.
-
Gebruik een aanrechttafel. Dit is wat ik zou doen. Ofwel in
bar
, of in een bijzettafel zoalsbar_foo_counter
, verkrijg een rij-ID metUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counter
of de minder efficiënte optie als uw framework
RETURNING
niet aankan :SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;
Dan, in dezelfde transactie , gebruik de gegenereerde tellerrij voor het
number
. Wanneer je commit, de tellertabelrij voor diebar_id
wordt ontgrendeld voor de volgende te gebruiken query. Als je terugdraait, wordt de wijziging genegeerd.
Ik raad de baliebenadering aan, met behulp van een speciale zijtafel voor de teller in plaats van een kolom toe te voegen aan bar
. Dat is schoner om te modelleren en betekent dat u minder update-bloat in bar
creëert , wat zoekopdrachten kan vertragen tot bar
.