Markland Hill Get in touch
Devlog

Billed three times, to the same second

· New Horizons Developments the builder

The admin ledger showed one night’s company overheads charged three times over. £1,196,250, three times, to the same second, all three reporting the same balance afterwards — and sixty duplicate overdraft interest lines sitting beside them.

Both of those are charged by reading the player. And the app opens a screen by firing several requests at once.

So each request read the same catch-up marker, each worked out the same window of unbilled nights, and each billed it. Nothing else in the game duplicated, because nothing else bills on a read.

That is the actual lesson, and it is worth stating on its own: an endpoint that charges money as a side effect of being read is not safe to call twice, and something will always call it twice. A retry will. A double tap will. A screen that loads three panels will.

The fix is to claim the marker before the money moves. The update carries the old value in its WHERE clause, and the caller only charges if a row actually changed. Read-committed isolation means the losing requests re-check against the new marker and find nothing owed.

That is a compare-and-swap, which is a very old idea, and it is the right one here precisely because it needs no coordination between the requests — they do not have to know about each other, they just have to lose a race gracefully.

Three requests still arrive. One of them pays.

← All posts