Skip to content

Archive released gear-station rows so station clicks stop freezing the server - #66

Merged
XxFran10xX merged 2 commits into
mainfrom
perf/gear-station-archive
Oct 21, 2026
Merged

XxFran10xX merged 2 commits into
mainfrom
perf/gear-station-archive

Conversation

@XxFran10xX

Copy link
Copy Markdown
Contributor

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.

  • The file is huge. Main's plugins/Magic/data/gear-stations.yml is 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.
  • Every lookup re-reads all of it. stations() reads and parses the whole file again, then writes each row out to YAML and back in. occupied(), station(id) and patch() each call it, so one click repeats this several times.
  • It is close to breaking. GearYaml.read has 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:

    • it is released;
    • it has valid ownership (format 1, CHARACTER / OWNERLESS / HELD);
    • it has a valid occupancy-id;
    • no file in gear-ownership/{estates,migration,receipts/*} refers to it.

    The steps:

    1. Each row is first written to data/gear-stations-archive/<occupancy-id>.yml, without overwriting, and synced to disk along with its folders.
    2. If an identical archive copy already exists, it is synced to disk again before the row is removed.
    3. The live file is re-read and every archived row is checked to be unchanged. Then the live file is rewritten once without those rows.
    4. A row that fails any step (a different copy already exists, an I/O error, a missing ownership kind, or it changed meanwhile) stays in the live file and is logged. Other rows are not affected.
    5. The whole pass aborts without changes only when an ownership journal or receipt cannot be read.
  • When it runs: at load() (after holdInterrupted and migrate, before loadViews) and after each successful acknowledgeRelease. 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 uses GearYaml.copy/equal, so the byte-for-byte checks that detect a changed station work as before.

  • Chunk scans: SacrificeTargeting and MeditationCircle now skip unloaded chunks instead of loading them with getChunkAt. 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 second acknowledgeRelease still 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

  • Linux mvn verify: 695 tests, 0 failures, all coverage checks met.
  • New tests:
    • GearStationArchiveTest and GearStationArchiveCoverageTest;
    • an interrupted sync after the archive copy is created;
    • a failed copy only skips its own row;
    • a row changing while it is being archived;
    • a missing or null ownership kind;
    • unloaded-chunk tests for the sacrifice and meditation scans.
  • Reviewed by GPT-6.1-Sol before this PR:
    • round 1: two P2 findings (an archive copy reused before it was synced to disk; a missing kind crashing every later pass), both fixed;
    • round 2: VERDICT: APPROVE.
  • Dev bot test: archive step on startup plus a deposit and eject. Results will be posted here before this is marked ready.

🤖 Generated with Claude Code

XxFran10xX and others added 2 commits October 21, 2026 16:03
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>
@XxFran10xX

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1a966b53-f0de-4368-a1fa-57afddb2370b
📥 Commits

Reviewing files that changed from the base of the PR and between 464c85c and 5855983.

📒 Files selected for processing: 13
  • src/main/java/net/tfminecraft/magic/artifact/sacrifice/SacrificeTargeting.java
  • src/main/java/net/tfminecraft/magic/gear/GearStationStore.java
  • src/main/java/net/tfminecraft/magic/gear/ownership/GearFiles.java
  • src/main/java/net/tfminecraft/magic/gear/ownership/GearPropertyStore.java
  • src/main/java/net/tfminecraft/magic/meditation/MeditationCircle.java
  • src/test/java/net/tfminecraft/magic/GearOpusRegressionTest.java
  • src/test/java/net/tfminecraft/magic/GearReviewRegressionTest.java
  • src/test/java/net/tfminecraft/magic/GearStationArchiveCoverageTest.java
  • src/test/java/net/tfminecraft/magic/GearStationCoverageSupport.java
  • src/test/java/net/tfminecraft/magic/GearStationOwnershipBoundaryTest.java
  • src/test/java/net/tfminecraft/magic/MeditationCircleTest.java
  • src/test/java/net/tfminecraft/magic/artifact/sacrifice/SacrificeTargetingTest.java
  • src/test/java/net/tfminecraft/magic/gear/ownership/GearStationArchiveTest.java

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Summary

Summary by CodeRabbit

  • Bug Fixes

    • Prevented nearby-object detection from loading previously unloaded world chunks.
    • Released gear-station records are now safely archived during release handling and startup.
    • Live records are retained when archive creation, ownership verification, or data consistency checks fail.
    • Archive operations now preserve data integrity and avoid removing records until archived copies are durable.
  • Tests

    • Expanded coverage for unloaded areas, archive recovery, ownership validation, conflicts, failures, and repeated archive operations.

The change archives eligible released gear-station rows during release acknowledgement and startup. It also updates nearby-furniture scans to skip unloaded chunks, with tests covering both behaviours.

Changes: Gear station archiving

Layer / File(s) Summary
Archive eligibility and durable removal
src/main/java/net/tfminecraft/magic/gear/ownership/GearPropertyStore.java, src/main/java/net/tfminecraft/magic/gear/ownership/GearFiles.java, src/test/java/net/tfminecraft/magic/gear/ownership/GearStationArchiveTest.java
GearPropertyStore archives released rows with valid, settled ownership and no journal evidence. It verifies archive copies and durability before removing live rows. Failed checks and changed rows remain live.
Release and startup integration
src/main/java/net/tfminecraft/magic/gear/GearStationStore.java, src/test/java/net/tfminecraft/magic/GearStationArchiveCoverageTest.java, src/test/java/net/tfminecraft/magic/GearStationCoverageSupport.java, src/test/java/net/tfminecraft/magic/GearOpusRegressionTest.java, src/test/java/net/tfminecraft/magic/GearReviewRegressionTest.java, src/test/java/net/tfminecraft/magic/GearStationOwnershipBoundaryTest.java
The store archives after release acknowledgement and during startup after migration. Tests check archive creation, retained rows, logging, and archived effect state.

Unloaded chunk checks

Layer / File(s) Summary
Skip unloaded chunks during scans
src/main/java/net/tfminecraft/magic/meditation/MeditationCircle.java, src/main/java/net/tfminecraft/magic/artifact/sacrifice/SacrificeTargeting.java, src/test/java/net/tfminecraft/magic/MeditationCircleTest.java, src/test/java/net/tfminecraft/magic/artifact/sacrifice/SacrificeTargetingTest.java
Both nearby-furniture scans skip unloaded chunks. Tests verify that the scans do not load those chunks.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant GearStationStore
  participant GearPropertyStore
  participant FileSystem
  GearStationStore->>GearPropertyStore: archiveReleased()
  GearPropertyStore->>FileSystem: create or verify archive copy
  GearPropertyStore->>FileSystem: force archive and directory data
  GearPropertyStore->>FileSystem: remove eligible row from live store
Loading

Suggested reviewers: ryanbarlow97

Merge Risk: 🔵 Low · up to 58559

Station releases may slow as ownership history grows. The change is mergeable with awareness of that scaling risk.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size:M 30-99 effective changed lines (tests excluded in mixed PRs). label Oct 21, 2026
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@XxFran10xX

Copy link
Copy Markdown
Contributor Author

Dev test (TFMCDev01, head 5855983): pass

  • Startup: Dev's gear-stations.yml was 106,003 bytes with 8 released rows. The log showed Archived 8 released gear station rows to data/gear-stations-archive. The live file went to stations: {}, and all 8 archive files match the original rows. There were no Kept released, Could not archive or Gear property held lines.
  • Second restart: no new archive line. The live file and all 11 archive files were byte-identical afterwards.
  • TestChar2 station flow (craft → charge → abort → craft again at the same spot → abort):
    • Each abort refunded everything.
    • Each aborted row got its own archive file and left the live file within the same click.
    • Re-depositing at the same spot was not reported as occupied.
    • The bot saw each click answered in about 200–460 ms round trip, with no Magic warnings.
  • Meditation scan: detection still works. Sitting in a pedestal ring gave "Can't meditate here, no artifacts".

@XxFran10xX
XxFran10xX marked this pull request as ready for review October 21, 2026 14:28
@XxFran10xX
XxFran10xX merged commit 006108b into main Oct 21, 2026
4 checks passed
@XxFran10xX
XxFran10xX deleted the perf/gear-station-archive branch October 21, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 effective changed lines (tests excluded in mixed PRs).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant