Commit 24abf66
committed
feat: replace credit balance cache with per-expiration buckets
Replaces the previous scalar cache + lazy-recompute design with a bucket
cache keyed by (address, expiration_date), maintained eagerly by the
credit_history writers under a sort-by-expiration policy. The API read
path is now a single SELECT with no writes, no FIFO walk, and no cache
invalidation logic.
Key changes:
- New credit_balances schema: (address, expiration_date, amount, last_update)
with composite PK. The "no expiration" case uses a sentinel timestamptz
(9999-12-31) so the column can stay NOT NULL.
- Writers (distribution, expense, transfer) update buckets atomically in
the same transaction as the credit_history insert.
Expenses drain the soonest-expiring non-expired bucket first under
SELECT ... FOR UPDATE, serialising concurrent writers per address.
- Transfers compute recipient bucket expirations by capping each
consumed source bucket at min(source_expiration, requested_expiration),
preserving the existing re-transfer guard.
- New background task (CreditExpirationTask) sleeps until the next
pending expiration timestamp and deletes expired buckets when it
fires. Writers wake the task via a module-level asyncio.Event when
they insert a bucket whose expiration may be sooner than the one
currently being awaited. Expirations remain implicit (no synthetic
credit_history rows).
- get_credit_balance / get_credit_balance_with_details are pure SELECTs:
no FIFO recompute, no write-back. _apply_fifo_consumption and
_calculate_credit_balance_fifo are removed.
- Repair (aleph.repair.repair_node) now also bootstraps and reconciles
the bucket cache from credit_history, runs on every startup, and is
idempotent. Replaces the previous one-shot script approach.
- Policy change: consumption now sorts by expiration_date (soonest
first) rather than by message_timestamp (oldest first). This loses
fewer credits to expiration when issuance order and expiration order
diverge. FIFO scenario 1 in tests was updated accordingly; scenario 2
remains unchanged since both policies produce the same result there.
The previous credit_balances rows are dropped by the migration; the
repair function repopulates them on the next startup from credit_history,
which is the source of truth.
Test plan covered:
- bucket helper unit tests (insert/increment, negative/under-funded
consumption, sort-by-expiration drain order, sentinel handling)
- writer tests (distribution buckets, expense drain order, transfer
cap, whitelisted sender, recipient inherits capped expiration)
- read path (sum non-expired buckets, with_details grouping, multi-
address listing + count)
- expiration task (deletes expired, ignores sentinel, wakes on signal)
- repair (rebuilds buckets from history, idempotent)
- existing transfer / cap / expiration tests remain green under the new
semantics
- removed test_cache_invalidation_on_credit_expiration (no cache to
invalidate); replaced with a smaller test asserting that the read
query filters expired buckets directly1 parent 9e20d6d commit 24abf66
9 files changed
Lines changed: 1078 additions & 377 deletions
File tree
- deployment/migrations/versions
- src/aleph
- db
- accessors
- models
- services
- toolkit
- tests/db
Lines changed: 68 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
39 | 40 | | |
40 | 41 | | |
41 | 42 | | |
| |||
224 | 225 | | |
225 | 226 | | |
226 | 227 | | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
227 | 233 | | |
228 | 234 | | |
229 | 235 | | |
| |||
0 commit comments