feat(go): scheduled bank sync (Go only) #48

Merged
kacerr merged 1 commits from feat/scheduled-bank-sync into main 2026-10-08 12:27:14 +02:00
Owner

What

The Go server now runs the bank sync by itself on a schedule, defaulting to once a day. Until now the sync only ran when someone opened /sync-bank.

Go only, on purpose. The legacy Python app is being phased out and its multi-worker setup would need a cross-process lock for this. This is recorded in CLAUDE.md under "Deliberately Go-only features" and in a comment in legacy/app.py.

Behaviour

  • SYNC_SCHEDULE: comma-separated HH:MM times, default 04:00. off disables it. An invalid value stops the server at startup with an error.
  • SYNC_TZ: zone the times are read in, default Europe/Prague. The binary embeds the zone database, since the Alpine image has none.
  • Each scheduled run does what /sync-bank does: sync, infer, flush cache. It has a 10-minute timeout and logs the outcome.
  • Nothing runs at startup.
  • Manual and scheduled runs share one lock. A second request while one is running gets "a sync is already running". This also closes the existing gap where two overlapping manual runs could append the same rows.
  • The page footer shows the last sync since the server started (time, trigger, ok/FAILED) and the next scheduled run.

Deployment notes

  • It is on by default at 04:00 Europe/Prague once this image is deployed. Set SYNC_SCHEDULE=off to disable.
  • The schedule runs per process: the deployment must stay at one replica.
  • A local make web-go with real credentials also syncs at the scheduled time unless SYNC_SCHEDULE=off.
  • Without FIO_API_TOKEN_NEW / FIO_API_TOKEN_OLD it scrapes the transparent-account pages, as the manual sync does today.

Testing

  • New unit tests: schedule parsing, next-run calculation (including both DST switch days), run loop cancellation, runner status and busy handling, config loading, footer rendering.
  • make go-test-all (with -race) and make test pass. No new dependencies; no change to the JSON API or parity fixtures.
  • Live: server started with a schedule two minutes ahead ran one real scheduled sync (1 new transaction appended, 1 row inferred) and the footer updated.
  • Startup with SYNC_SCHEDULE=25:00 and with an unknown SYNC_TZ exits with a clear error; off logs that the schedule is disabled.
  • In the built container the schedule resolves to 04:00+02:00, confirming the embedded zone data works.
  • Not exercised: the "already running" path against a real sync (covered by a unit test only), and golangci-lint, which is still broken locally (T7).

🤖 Generated with Claude Code

## What The Go server now runs the bank sync by itself on a schedule, defaulting to once a day. Until now the sync only ran when someone opened `/sync-bank`. **Go only, on purpose.** The legacy Python app is being phased out and its multi-worker setup would need a cross-process lock for this. This is recorded in `CLAUDE.md` under "Deliberately Go-only features" and in a comment in `legacy/app.py`. ## Behaviour - `SYNC_SCHEDULE`: comma-separated `HH:MM` times, default `04:00`. `off` disables it. An invalid value stops the server at startup with an error. - `SYNC_TZ`: zone the times are read in, default `Europe/Prague`. The binary embeds the zone database, since the Alpine image has none. - Each scheduled run does what `/sync-bank` does: sync, infer, flush cache. It has a 10-minute timeout and logs the outcome. - Nothing runs at startup. - Manual and scheduled runs share one lock. A second request while one is running gets "a sync is already running". This also closes the existing gap where two overlapping manual runs could append the same rows. - The page footer shows the last sync since the server started (time, trigger, ok/FAILED) and the next scheduled run. ## Deployment notes - It is **on by default** at 04:00 Europe/Prague once this image is deployed. Set `SYNC_SCHEDULE=off` to disable. - The schedule runs per process: the deployment must stay at one replica. - A local `make web-go` with real credentials also syncs at the scheduled time unless `SYNC_SCHEDULE=off`. - Without `FIO_API_TOKEN_NEW` / `FIO_API_TOKEN_OLD` it scrapes the transparent-account pages, as the manual sync does today. ## Testing - New unit tests: schedule parsing, next-run calculation (including both DST switch days), run loop cancellation, runner status and busy handling, config loading, footer rendering. - `make go-test-all` (with `-race`) and `make test` pass. No new dependencies; no change to the JSON API or parity fixtures. - Live: server started with a schedule two minutes ahead ran one real scheduled sync (1 new transaction appended, 1 row inferred) and the footer updated. - Startup with `SYNC_SCHEDULE=25:00` and with an unknown `SYNC_TZ` exits with a clear error; `off` logs that the schedule is disabled. - In the built container the schedule resolves to 04:00+02:00, confirming the embedded zone data works. - Not exercised: the "already running" path against a real sync (covered by a unit test only), and `golangci-lint`, which is still broken locally (T7). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kacerr added 1 commit 2026-10-08 12:17:16 +02:00
The Go server now runs sync -> infer -> flush-cache by itself at the
times in SYNC_SCHEDULE (comma-separated HH:MM, default 04:00, "off"
disables), read in SYNC_TZ (default Europe/Prague). Nothing runs at
startup; an invalid value stops the server with an error.

- internal/scheduler: time-of-day parsing, Next, and the run loop
- banksync.Runner: one lock shared by /sync-bank and the scheduler so
  runs never overlap, plus the status of the last run
- footer shows the last sync since start and the next scheduled run
- binary embeds time/tzdata because the Alpine image has no zone data

Deliberately not implemented in the legacy Python app, which is being
phased out; recorded in CLAUDE.md under "Deliberately Go-only features".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kacerr merged commit d5da993690 into main 2026-10-08 12:27:14 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kacerr/fuj-management#48