Comment

Comment by mrkeen

> Revolut would own the database in my example.

If you have a protocol for interacting with your own database, you either need to rule out interacting with other systems (i.e. you have Revolut credits and you can only use them in the Revolut store,) or create a second protocol for inter-system transfers, in which case you have two protocols, and that second protocol won't play nicely with the first - e.g. partner bank informs you about a several new money transfers and a chargeback, meanwhile you're taking locks and directly setting balances. If the inter-system protocol is good enough, it will do everything the owned-locked-crud-database protocol would, and the crud is just going to get in the way.

You also have the problem of owning 'one thing'. If you lose 'one thing', you've wiped out all your records. Not to mention regular downtime from patching/releases etc. If for some reason you have the chance to use a database which presents itself as one-thing (but is distributed behind the scenes and has redundant replaceable parts) then whoever implemented that database is at the minimum using some kind of WAL (the 'ledger' of the database world), because those folks know they can't just have multiple masters CRUDding each other willy-nilly.

> How would a ledger work without having some kind of lock, how would you prevent a negative balance?

I wouldn't try to initially (but do see below). If the system can keep your balance from going below 0, then by CAP it's not available. If your supermarket loses connectivity or you're buying a cocktail while flying in a plane, your payment would just fail.

> When someone checks their balance, would you to retrieve every transaction from the ledger and calculate the current balance?

Pretty much everything in the concurrent space boils down to avoiding shared-mutable state. If you have it, your code is probably broken. Locking, ownership, defensive copies, immutability, etc. are all efforts to stay sane. If you go down the immutability path, more options open up. I use Kafka as the example. You append one new fact every time a money transfer happens. From a performance POV, Kafka scales horizontally, and appends only need negligible locking. I mention Kafka specifically because it also acts as the distribution. Different independent systems can follow it in near real-time, and they can choose to store and present the current balances, as a running calculation. And it works as long as you don't go back and edit history (corrections are appended instead.) As long as the kafka records are intact, you can always rebuild any other derived data from it.

If you did really need to forbid less than zero, you can model that in your events. Instead of emitting a MoneyMoved event, you could emit a MoveMoneyIfAvailable event. The reader of the system would see that event strictly in order (this is the key bit - you're basically using Kafka to gain linearisability in ACID-speak) and either emit a MoneyMoved event or a MoneyMoveRejected event in response. But again, this last trick doesn't mean you can order cocktails on a plane (if you tried to emit a MoveMoneyIfAvailable from up there, it wouldn't reach the server, and you also couldn't get the yes/no back anyway).

parent mrkeen 10 days ago

No comments yet

Be the first to share your thoughts.

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton