Skip to content

fix(station): stop unlistable cores from holding tape retention open - #1004

Merged
guyverino merged 1 commit into
mainfrom
fix/station-retention-barrier
Oct 16, 2026
Merged

guyverino merged 1 commit into
mainfrom
fix/station-retention-barrier

Conversation

@guyverino

Copy link
Copy Markdown
Collaborator

What & why

A station's tape retention trimmed 0 spans every hour, so tape_recorder.sqlite never shrank — neither the 180-day limit nor "do not record emulator trades" deleted anything.

Retention waits until every required core has listed its closed trades (the barrier catchup::listings_complete), so a purge cannot delete tape a not-yet-known trade needs. After 10 minutes it gives up and runs conservatively: every key counts as incomplete and keep_map keeps all tape. The station log showed that fallback on every pass (trim always exactly 10 minutes after the hourly start).

Two kinds of core could never list, so the barrier never closed:

  • a core on a platform the exchange directory does not know (venue(code) == None, e.g. Aster): Session::list_tape_catchup returned before listing;
  • a server switched off in the config: require_listings was fed config_order (every configured server), but only active servers get a session.

Changes:

  • list_tape_catchup settles a venue-less core's listing at once (catchup::listed_empty) — no row of it can name a venue, so its listing protects nothing.
  • require_listings gets the ids of existing sessions (connected or not) instead of every configured server.
  • When the 10-minute fallback still fires, the tape log names the cores that held it (retention: listings incomplete after 10 min, nothing is deleted this pass; cores not listed: [...]).

Verified on the production station (dev build): the first pass after the deploy trimmed 4397 spans two minutes after start instead of 0, a following one 18936; the file went from 1.16 GB to 245 MB.

Notable decisions

  • Tape on the markets of such cores now goes by age alone (owner's decision). Their trades are never known to retention, so one straddling the retention edge loses its older part, and their emulator tape stays until it ages out — the same trade-off already accepted for a live close on an unknown venue.
  • listed_empty applies the completion synchronously: the queued listed only orders completion behind submitted pages, and an empty listing has none.

Known limitations

  • The first purge after months of nothing to delete is large: on a 1 vCPU / 1 GB server it starved the process for ~75 s (cores reconnected). A one-off for the backlog; hourly passes afterwards are small. The post-delete recount over all known trades (~140k here) still runs every hour and is the next candidate if hourly stalls show up.
  • No session-level test pins require_listings to sessions — there is no session harness in the unit tests; the retention-level test covers the barrier itself.

Issues

None of the 15 open issues is touched by this change.

How to verify

cargo build -p moon-ui-gpui --bin moonterminal --target x86_64-pc-windows-msvc --all-targets
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked --target x86_64-pc-windows-msvc -- -D warnings
cargo test --workspace --target x86_64-pc-windows-msvc

fmt exit 0, build ok, clippy zero warnings, all 23 test targets ok (moon-core 3309 passed). New tests: a_venue_less_core_does_not_hold_aged_tape (fails with "maintenance did not settle" without the fix), an_empty_listing_settles_the_barrier_at_once. FireTest not run: no chart/render/window/input change.

@guyverino
guyverino merged commit 9325dbd into main Oct 16, 2026
10 checks passed
@guyverino
guyverino deleted the fix/station-retention-barrier branch October 16, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant