def lock_bundles(cursor, transfer, bundles=None, prefer_bundles_from=-1): assert transfer.status == 'pre' tipper, tippee = transfer.tipper, transfer.tippee currency = transfer.amount.currency if bundles: # Refetch the bundles to ensure validity and prevent race conditions bundles = tuple(bundles) bundles = cursor.all( """ SELECT b.* FROM cash_bundles b WHERE b.owner = %(tipper)s AND b.withdrawal IS NULL AND b.locked_for IS NULL AND b.amount::currency = %(currency)s AND b.id IN %(bundles)s ORDER BY b.ts """, locals()) else: bundles = cursor.all( """ SELECT b.* FROM cash_bundles b JOIN exchanges e ON e.id = b.origin WHERE b.owner = %(tipper)s AND b.withdrawal IS NULL AND b.locked_for IS NULL AND b.amount::currency = %(currency)s ORDER BY b.origin = %(prefer_bundles_from)s DESC , e.participant = %(tippee)s DESC , b.ts """, locals()) transferable = sum(b.amount for b in bundles) x = transfer.amount if x > transferable: raise NegativeBalance() for b in bundles: if x >= b.amount: cursor.run( """ UPDATE cash_bundles SET locked_for = %s WHERE id = %s """, (transfer.id, b.id)) x -= b.amount if x == 0: break else: cursor.run( """ UPDATE cash_bundles SET amount = (amount - %s) WHERE id = %s; INSERT INTO cash_bundles (owner, origin, amount, ts, locked_for, wallet_id) VALUES (%s, %s, %s, %s, %s, %s); """, (x, b.id, transfer.tipper, b.origin, x, b.ts, transfer.id, b.wallet_id)) break
def check_balances(cursor): """Check that balances aren't becoming (more) negative """ oops = cursor.one(""" SELECT p.id , p.username , p2.balances FROM payday_participants p2 JOIN participants p ON p.id = p2.id WHERE (p2.balances).EUR < 0 OR (p2.balances).USD < 0 LIMIT 1 """) if oops: log(oops) raise NegativeBalance() log("Checked the balances.")
def lock_bundles(cursor, transfer, bundles=None, prefer_bundles_from=-1): assert transfer.status == 'pre' cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") tipper, tippee = transfer.tipper, transfer.tippee currency = transfer.amount.currency bundles = bundles or cursor.all( """ SELECT b.* FROM cash_bundles b JOIN exchanges e ON e.id = b.origin WHERE b.owner = %(tipper)s AND b.withdrawal IS NULL AND b.locked_for IS NULL AND b.amount::currency = %(currency)s ORDER BY b.origin = %(prefer_bundles_from)s DESC , e.participant = %(tippee)s DESC , b.ts """, locals()) transferable = sum(b.amount for b in bundles) x = transfer.amount if x > transferable: raise NegativeBalance() for b in bundles: if x >= b.amount: cursor.run( """ UPDATE cash_bundles SET locked_for = %s WHERE id = %s """, (transfer.id, b.id)) x -= b.amount if x == 0: break else: cursor.run( """ UPDATE cash_bundles SET amount = (amount - %s) WHERE id = %s; INSERT INTO cash_bundles (owner, origin, amount, ts, locked_for, wallet_id) VALUES (%s, %s, %s, %s, %s, %s); """, (x, b.id, transfer.tipper, b.origin, x, b.ts, transfer.id, b.wallet_id)) break
def check_balances(cursor): """Check that balances aren't becoming (more) negative """ oops = cursor.one(""" SELECT * FROM ( SELECT p.id , p.username , (p.balance + p2.new_balance - p2.old_balance) AS new_balance , p.balance AS cur_balance FROM payday_participants p2 JOIN participants p ON p.id = p2.id AND p2.new_balance <> p2.old_balance ) foo WHERE new_balance < 0 AND new_balance < cur_balance LIMIT 1 """) if oops: log(oops) raise NegativeBalance() log("Checked the balances.")