Repository navigation
Archive released gear-station rows so station clicks stop freezing the server - #66
Conversation
Every gear station click re-reads and parses gear-stations.yml. On Main the file is 5.4 MB, and 5.43 MB of that is released rows that nothing reads again, so a single deposit or eject froze the server for 0.5-2 s. The file was also heading for the 16M code point read limit, which would make every station unreadable. - At startup and after each acknowledged release, copy every released row with settled ownership to data/gear-stations-archive/<occupancy-id>.yml (no-clobber, fsynced) and only then drop it from the live file. A crash between the two steps just repeats the removal; a differing copy keeps the row and warns. Rows named by any ownership journal or receipt stay. - stations() no longer YAML round-trips every row: rows come from a fresh parse, so they are already private. Writes and comparisons still copy. - Shrine and meditation scans skip unloaded chunks instead of loading them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An archive copy found already published (a crash after its link but before its folder was synced) is synced again before its row leaves the live file. - An ownership map with a missing or null kind is simply not archived instead of throwing on every pass. - A row whose copy fails, differs, or changes while archiving stays live and is reported; the other rows are still archived. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@coderabbitai review |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Summary
Merge Risk: 🔵 Low · up to
|
✅ Action performedReview finished.
|
|
Dev test (TFMCDev01, head 5855983): pass
|
Why
A spark profile on Main (2026-10-21, https://spark.lucko.me/QVIonmUGd0) showed 0.5–2 s main-thread freezes on nearly every gear-station click (
GearStationListener.onInteract→GearStationStore.deposit/install/save→GearPropertyStore.stations()/patch→GearYaml.bytes/copy). They added up to about 40 s in 23 minutes.plugins/Magic/data/gear-stations.ymlis 5.44 MB, and 5.43 MB of it is released rows: 39 rows of about 200 KB each, carrying deposit, charge and release intents with item bytes. The one active row is 4 KB.stations()reads and parses the whole file again, then writes each row out to YAML and back in.occupied(),station(id)andpatch()each call it, so one click repeats this several times.GearYaml.readhas a 16M code point limit. About 50 more releases would make the file unreadable, and every gear station would stop working.Change
Archive released rows (
GearPropertyStore.archiveReleased()). A row is archived only when all of these hold:gear-ownership/{estates,migration,receipts/*}refers to it.The steps:
data/gear-stations-archive/<occupancy-id>.yml, without overwriting, and synced to disk along with its folders.When it runs: at
load()(afterholdInterruptedandmigrate, beforeloadViews) and after each successfulacknowledgeRelease. A failure only warns. It never changes the release result and never marks the store unreadable.Lookups:
stations()no longer runs each row through a YAML write-and-parse copy, because rows come from a fresh parse on every call. Every write and comparison still usesGearYaml.copy/equal, so the byte-for-byte checks that detect a changed station work as before.Chunk scans:
SacrificeTargetingandMeditationCirclenow skip unloaded chunks instead of loading them withgetChunkAt. Both scan the 3×3 chunks around a player, so nothing changes in practice.Behaviour note: after a release is acknowledged, its row is no longer in the live file.
station(id)for that released id now throws "Occupancy disappeared", and a secondacknowledgeReleasestill returns false. No production path looks up a released id after acknowledging it. Every reader of released rows (estate, migrate, requireReady, describe, holdInterrupted, GearEffects, the duplicate checks) already skips them.Testing
mvn verify: 695 tests, 0 failures, all coverage checks met.GearStationArchiveTestandGearStationArchiveCoverageTest;VERDICT: APPROVE.🤖 Generated with Claude Code