Bulk database writes now stream in small transactions targeting ~10ms
each (adaptive chunk sizing), designed for an ultimate load of one real
day per simulated day: writes trickle continuously instead of arriving
as monolithic bursts that hold locks and demand oversized hardware.
Daily accrual postings are collected in memory under the sim lock and
written to the ledger lock-free (collectAccrualMovements +
writeMovementsChunked); accrual_state persistence commits in the same
~10ms chunks.
This removes the 26–37s dashboard stalls seen on PostgreSQL while the
simulation ran (a lock convoy: dashboard → state lock → customer adder
→ sim lock → day sweep doing one fsync'd transaction per account).
Measured with the sim running and a 30,000-customer add concurrently:
dashboard polls hold 51–68ms with no stalls, and customers add at ~190/s
while simulated days tick ~10s/day at 30k customers.
Known remaining spike: month-end interest application movements are
still written per account inside the products engine under the sim
lock; streaming those needs a gobank-products change.
[0.3.43] - 2026-08-27
Hetzner cloud deployment for performance testing: deploy/hetzner/ plus
Taskfile targets cloud:up / cloud:down / cloud:status / cloud:ssh
(run via tp secrets). cloud:up SCALE=small|medium|large|xl (or any
hcloud server type) provisions a firewalled server via cloud-init with
PostgreSQL sized to the machine's RAM, cross-compiles the demo (arm64
for cax types), and deploys it as a systemd service on port 1347.
Deleting the server (cloud:down) is what stops billing.
The demo server honours GOBANK_PG_DSN (real PostgreSQL backend via
pgx) and GOBANK_ADDR (fixed listen address). A configured DSN is
pinged at startup so an unreachable database fails fast, and in
Postgres mode startup drops all public tables — in-memory simulation
state is authoritative and stale rows from a previous run would
collide; export .goluca to keep a run's ledger.
The About → Runtime page reports the actual data store (previously
hardcoded "In-memory (pglike/SQLite)") with the DSN password redacted.
The DB explorer now works on real PostgreSQL: catalog access goes
through backend-aware helpers (sqlite_master/PRAGMA on pglike,
pg_tables/information_schema/pg_indexes on PostgreSQL).
Customer creation is transactional: each customer's writes (customer +
PII rows, ledger accounts, funding movements) share one transaction via
go-luca v0.2.32 SQLLedger.WithTx and gobanks-customers v0.2.1
SQLCustomerStore.WithTx — 333 customers/s vs 178 on a cx33 against
PostgreSQL. A customer whose persist fails is rolled back and skipped
instead of surviving in memory only.
The 1ms per-customer yield in batch adds now only runs on WASM builds
(runtime.GOOS == "js"); server builds add customers at full speed.
[0.3.42] - 2026-08-26
The database now carries complete interest state: every simulated day
the per-account accrual numerators (and the BoE accumulator) persist
to a new accrual_state table, written off the state locks. An
accrued_pounds_e7 column models each accrual as 7dp pounds (integer
fixed point, rounded from the exact numerator; the pence conversion
truncates, matching engine application). refreshFromLedger rehydrates
engine numerators, account mirrors and BoE state after an import.
Daily accruals are visible in the ledger: newly accrued whole pence
post daily (code LDAS:FTDP:ACRU) from Expense:Interest /
Income:Interest into AccruedInterest holding accounts, reversed on
month-end application so net P&L stays exactly the engine's.
BoE reserve interest is modelled in accounts: Income:Interest:BoE
accrues daily into Asset:AccruedInterest:BoE and is received into
Asset:BoEReserves at month end; reports show applied plus accrued.
DB explorer: per-table option to truncate ID/text cells to 10 chars
with the full value as hover tooltip (?trunc=1), persisted across
sort and pagination links.
Bank app front door no longer lists every customer: /app/ is a mock
login screen (customer ID plus any password) and unknown IDs are
rejected; customer names no longer appear pre-login.
[0.3.41] - 2026-08-26
Interest now runs on the gobank-products v0.1.10 engine: exact integer
daily accrual in memory (numerator over 10,000 × 365, remainder carried
— sub-penny interest is never lost) with one application movement per
account at month end. Balances step up monthly; reports include
accrued-but-unapplied interest. The demo's own float64 interest loop
and pending-movement flusher are gone.
Blanket prohibition on float64 for money storage: all money is integer
minor units (luca.Amount) across accounts, payments, tx log, histories,
dashboards, treasury/gilts, P&L and the bank-app JSON (now *_minor
fields). fmtMoney takes pence, fixing its rounding-carry bug.
TestNoFloatMoneyStorage guards against regressions.
Performance: a simulated day at 10k accounts drops from ~3.1s to ~5ms
(end-of-day sweep is query-free); WASM full-year integration run drops
19.3s → 2.0s. Month-end application sweeps are paced on WASM so the
page stays responsive; daily interest ledger writes no longer hold the
state lock (worst-case dashboard lock wait ~2.7s → under 2ms).
Customer Count chart: integer y-axis labels; x-axis with pinned
YYYY-mm-dd end labels, calendar-boundary mid labels and day/week/month
minor tick marks (via a local go-analyze/charts fork adding
XAxisOption.CustomTicks); collision handling so date labels never
overlap, regression-scanned across 1500 spans.
[0.3.40] - 2026-08-25
Fix daily interest postings never reaching the ledger database: demo
only looked up Expense:Interest / Income:Interest (relying on a
gobank-products EnsureInterestAccounts that does not exist), so the
cached account IDs stayed empty and every posting was silently skipped.
initLedger now creates the accounts when missing.
Flush pending interest movements to SQL at each end-of-day finalize
(one batch per day) instead of only before export, so they show as
normal transactions in the DB explorer.
Bump gobank-products pin v0.1.8 → v0.1.9 (v0.1.8 tag pins an
unresolvable go-luca).
Fix wasm crash when adding 100 customers: the demo DSN
file::memory: was only pool-safe for a single connection — go-postgres
special-cased just :memory:, so a second pool connection (opened when
dashboard polling overlaps a batch add) saw its own empty database.
Fixed in go-postgres v0.5.7 (all in-memory DSN spellings now share one
database); demo pin bumped v0.5.5 → v0.5.7, regression test added.
Fix native (server-mode) startup: layout templates still used pongo2
syntax after lofigui's switch to html/template; results now passed as
template.HTML.
Fix "database is locked" errors under concurrent load (seen natively
once pool connections genuinely shared one database): go-postgres
v0.5.8 opens the shared temp file with immediate transactions, WAL and
a busy timeout, so read-then-write transactions no longer hit SQLite's
handler-bypassing SQLITE_BUSY deadlock-avoidance path. Pin bumped
v0.5.7 → v0.5.8.
Fix the browser (WASM) crash on adding customers: go-postgres's
single-shared-connection fallback only locked per driver call, so
concurrent pool "connections" interleaved statements on the one SQLite
connection and corrupted it (panic inside SQLite). go-postgres v0.5.9
holds the lock across whole transactions and open result sets; pin
bumped v0.5.8 → v0.5.9.
Drop the deprecated ncruces/go-sqlite3/embed blank import that printed
"you're unnecessarily importing ... embed" at startup.
Fix a WASM deadlock ("all goroutines are asleep") when the DB explorer
queried while another query's rows were open: go-postgres v0.5.10 now
materialises query results instead of holding the shared-connection
lock until rows close. Pin bumped v0.5.9 → v0.5.10.
Fix task test:wasm on modern Node: wasm_test.js/wasm_bench.js no
longer assign the getter-only globalThis.crypto.
Fix the browser page freezing and eventually being killed by Chrome
(no console error) once ~100 customers were added: the simulation used
a fixed-rate 200ms ticker, and in WASM once advanceDay takes longer
than the interval the next tick is always due, the Go scheduler never
goes idle, and control never returns to the JS event loop — UI frozen,
days advancing flat-out, memory growing until the tab dies. The sim
(and payments) loops now self-pace: wait 200ms after each day
completes. Verified by driving the built wasm in headless Chrome via
CDP: page stays responsive with 130+ customers and stable memory.
[0.3.39] - 2026-03-26
Add RC deploy site and fix tp check warnings
[0.3.38] - 2026-03-25
adding extra files
[0.3.37] - 2026-03-20
Adding memory management
[0.3.36] - 2026-03-19
Adding app functionality to wasm
[0.3.35] - 2026-03-19
New DB and updating customer view
[0.3.34] - 2026-03-18
(no changes recorded)
[0.3.33] - 2026-03-18
adding benchmark
[0.3.32] - 2026-03-18
fixing db
[0.3.31] - 2026-03-18
Adding customer app preview
[0.3.30] - 2026-03-17
Working on documenation and interest
[0.3.29] - 2026-03-16
moving simulation and products to gobank-products
[0.3.28] - 2026-03-16
tidying
[0.3.27] - 2026-03-16
Adding roles and movement codes
[0.3.26] - 2026-03-16
unifying DB
[0.3.25] - 2026-03-16
fixing menu and db explorer in WASM
[0.3.24] - 2026-03-16
db explorer
[0.3.23] - 2026-03-16
fix import export buttons
[0.3.22] - 2026-03-16
tweak check
[0.3.21] - 2026-03-14
Removing .task from vc
[0.3.20] - 2026-03-14
Updating to goluca
[0.3.19] - 2026-03-07
Adding software hierarchy to docs
[0.3.18] - 2026-03-07
Adding version
[0.3.17] - 2026-03-07
Concept of blue green dB releases and unfying metatdate documentation