Commit 84ecbb5
committed
fix(SCHEMA_VERSION 5->6): one-time billable_requests backfill, plus a real destructive-wipe landmine
Closes the boot-time double/under-billing bug in busbarAI core's
governance::state::hydrate_budgets: that function used a value-based
heuristic (billable_requests==0 && requests>0 => "legacy pre-split
row, re-seed it") to detect data needing a one-time backfill - but
that exact counter shape is ALSO what a bucket looks like when every
request in the window was legitimately refunded (refund_bucket
decrements billable_requests but never requests, by design). The
heuristic ran on every boot and could not tell the two cases apart,
silently re-billing correctly-refunded fees on restart.
Fix: do the backfill ONCE, durably, gated on this store's own
schema-version crossing (which only ever happens once, ever, in a
store's lifetime) instead of on every boot. This is safe as an
unconditional blanket UPDATE only because 1.5.0/SCHEMA_VERSION 5 has
never shipped to a real customer - there is no genuine "currently,
legitimately refunded to zero" row in existence yet that this could
incorrectly re-bill. After this lands in busbarAI core, hydrate_budgets
drops the heuristic entirely and trusts billable_requests
unconditionally, forever - v6+ rows are written correctly from birth.
Found and fixed a second, more serious pre-existing bug while
implementing this: migrate()'s legacy-drop detection checked for
'keys'/'store_meta' among its "needs a destructive wipe" signals -
but those are the CURRENT (v5+) table names, not just old ones, so a
real v5 database legitimately has both. Naively bumping SCHEMA_VERSION
to 6 under the existing `version < SCHEMA_VERSION` gate would have
made every real v5 store's OWN live data register as "legacy" on the
v5->v6 crossing and wiped it - the exact opposite of what this backfill
is supposed to do. Fixed by gating the destructive path on the actual
pre-v5 boundary (`version < 5`, the boundary that was ALWAYS destructive
by design) instead of on SCHEMA_VERSION (which moves every bump), and
gating the new additive backfill separately on `version < 6`.
Both fixes proven red-before-green by hand: reverting the has_legacy
gate back to `< SCHEMA_VERSION` reproduces real data loss in
migrate_v5_to_v6_backfills_billable_requests_without_wiping_data;
reverting has_legacy's table list to exclude 'keys'/'store_meta'
breaks the PRE-EXISTING migrate_drops_and_recreates_a_genuinely_older_schema
test (a real, older schema no longer gets detected/dropped).
Full gate: fmt clean, clippy clean, cargo test --workspace (49 lib +
3 e2e) all green.1 parent 8d0ae0c commit 84ecbb5
2 files changed
Lines changed: 130 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
43 | | - | |
44 | | - | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
45 | 51 | | |
46 | 52 | | |
47 | 53 | | |
| |||
387 | 393 | | |
388 | 394 | | |
389 | 395 | | |
390 | | - | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
391 | 411 | | |
392 | 412 | | |
393 | 413 | | |
394 | | - | |
| 414 | + | |
395 | 415 | | |
396 | 416 | | |
397 | 417 | | |
| |||
424 | 444 | | |
425 | 445 | | |
426 | 446 | | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
427 | 476 | | |
428 | 477 | | |
429 | 478 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
929 | 929 | | |
930 | 930 | | |
931 | 931 | | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
932 | 1008 | | |
933 | 1009 | | |
934 | 1010 | | |
| |||
0 commit comments