34 ms·
This approach is good if you do not have to apply some check on the balance before inserting a new transaction. If you do (say verifying that balance does not g
by eveningcoffee 10y ago
This approach is good if you do not have to apply some check on the balance before inserting a new transaction. If you do (say verifying that balance does not go negative) then you have at least two options:
1) you make transactions serialized
BEGIN ISOLATION LEVEL SERIALIZABLE;
select sum(amount) as balance from transaction where user_id = 1;
-- do something with balance
insert into transaction values (1, -100);
COMMIT;
2) or you use row level locking on an additional table (say user, while this conflicts with the system table user, but for the sake of the example we assume this is not the case) to serialize the access.
BEGIN;
select user_id from user where user_id = 1 for update;
select sum(amount) as balance from transaction where user_id = 1;
-- do something with balance
insert into transaction values (1, -100);
COMMIT;
Now it may be tempting to change this to
BEGIN;
select balance from user where user_id = 1 for update;
-- do something with balance
update user set balance = balance + (-100) where user_id = 1;
insert into transaction values (1, -100);
COMMIT;
Just for clarity: in most cases you should register your transaction event anyway (with a time stamp) for later auditing, but you now will have a risk of introducing inconsistencies between calculated balance and transactions due to the programming errors (that is partially committed transactions).
Also you should obviously create proper primary keys and indexes.
Also you should include proper error/exception handling.
Also if you already do locking, then you could also consider different locking strategies described in the article.
- dalore 10y agoAnother solution would be to have a view that calculates the balance from the transactions. Then your sql code can check the balance without doing a sum (which might get expensive, and often the same value until a transaction changes it) just by joining to that view. i.e. `WHERE total_view.balance > 0`