BQL balance query can read stale state immediately after add_entry #37
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Calling
GET /libra/api/v1/balance(or any BQL-backed balance endpoint) in the same request flow asPOST /libra/api/v1/entries/...can return a balance that doesn't yet include the just-posted entry. Fava lazily reloads the.beancountfile and a balance call within ~50ms of anadd_entryreads a stale view.Reproduce
Most cleanly visible in tests, but reproducible by hand:
For the test harness, the workaround that consistently fixes it is to call
GET /libra/api/v1/entries/user(which triggers a Fava/journalread and forces a reload) between the post and the balance read. The smoke-test approve flow benefits from the same effect — the approve handler reads the journal to find the pending entry, which warms Fava, and the subsequent balance call is clean.Why this matters
Two categories of risk for production:
Possible directions
a. Cache invalidation on write. Have
FavaClient.add_entryflush whatever in-memory state the balance query reads from before returning. Cleanest in principle but requires understanding Fava's internal caching.b. Explicit reload call after
add_entry. Fava exposes/api/changedand probably an internal way to force a watcher cycle. Cheaper than (a) but may still have a small window.c. Document and require client retry. Accept the eventual consistency window and have the balance endpoint accept a
?refresh=1(or similar) that forces a Fava reload before computing. Pushes the cost to callers that care.d. Server-side post-write barrier. Have the entry endpoints await one Fava read after the write before returning the response. Latency cost on every post but trivially correct.
(d) is probably the right default — entry posts are not hot-path, balance reads are. Adding ~30-50ms to each post to guarantee read-your-writes is worth it.
Workaround in place
Test harness fixtures pre-flight a
list_user_entriescall before any balance assertion. Production callers should be aware until a server-side fix lands.Scope
fava_client.py: add_entry(and friends) — add a post-write sync step.views_api.py: api_get_balance/api_get_all_balances— consider an explicit refresh entry point.