Skip to content

perf: save vehicle checkpoints in one transaction and stop idle switch updates - #80

Merged
XxFran10xX merged 1 commit into
mainfrom
perf/checkpoint-batch-and-switch-tick
Oct 23, 2026
Merged

XxFran10xX merged 1 commit into
mainfrom
perf/checkpoint-batch-and-switch-tick

Conversation

@XxFran10xX

Copy link
Copy Markdown
Contributor

Summary

Two main-thread costs found in a spark profile of Main (2026-10-22, 20:28–21:28 UTC):

1. The 5-minute vehicle checkpoint froze the server for 0.3–0.6 s, up to about 1 s.
Every 5 minutes, "Checkpointing vehicles..." ran on the server thread. It saved each live vehicle as a separate SQLite commit, each waiting for the disk, then took a full VACUUM INTO backup copy. The database is small (about 336 KB on Main), so the time is spent waiting on the disk, which is busy on the shared host. Players feel this as a lag/rubber-band blip every 5 minutes. Main's ticks peaked at 600 ms–3 s every 5 minutes.

  • VehiclePersistence.saveLiveBatch saves all live vehicles in one transaction, so a checkpoint waits for one commit instead of one per vehicle.
    • Per-vehicle failures are still reported and logged exactly as before ("Failed to persist … during checkpoint"). One vehicle failing doesn't stop the others.
    • If the transaction itself fails, it logs that and falls back to the old one-by-one saves, so nothing is lost.
  • The VACUUM INTO backup now runs on every 6th checkpoint: at startup, then every 30 minutes. The WAL checkpoint still runs every cycle. Backup retention is unchanged (KEEP = 3), so it now covers about 1.5 h instead of 15 min.
  • Saves stay on the server thread: moving them off it could let an older snapshot land after a newer one.

2. Rail-switch animation read the tags of every track display, every tick.
TrackDisplayManager.tickSwitches looped over every live rail display each tick and read its persistent data to find the few switch displays. It also re-teleported each switch every tick, even when it was already at its target yaw. At 21:13 on Main it took +1.5 s in one minute.

  • Switch displays are now kept in their own list, with their junction id. It is filled where switches spawn and pruned where they are removed, despawned or unloaded.
  • The tick reads no tags, and it skips the teleport when the stepped yaw is already the current yaw.
  • Behaviour is visually identical. Dead rail displays are still pruned from live on chunk load and unload, as before.

Testing

  • Full mvn -B clean verify locally: 1748 tests, 0 failures, 9 skipped. The skipped ones are POSIX-only tests that skip on Windows; the coverage gate reports 10 missed lines in those untouched classes only. The changed classes have 0 missed lines. Linux CI is the reference.
  • New and updated tests:
    • Batch save on real SQLite, where one vehicle with a broken entity fails and the others still commit and update the chunk index.
    • Fallback when the transaction throws (with or without a message).
    • Checkpoint timer: one batch call, failure logging, and the backup on the 1st and 7th run.
    • Switch tick: no tag reads, an idle switch never teleports, a thrown switch animates then stops, and a switch removed by chunk unload is never touched again.
  • TFMCDev01 (bot tests in ~/grave-hopper-test/, character bot TestChar1):
    • Checkpoint: 6 live fixed_artillery rows (3 reloaded from SQLite after a restart, 3 freshly spawned) all got the same updated_at from the 00:05:47 checkpoint, within 1 ms of each other. There were no "Failed to persist" or "checkpoint transaction failed" lines. The test then removed them with /vf kill.
    • Backups: one at startup (23:30:22), none at the five checkpoints from 23:35 to 23:55 (the old jar took one every 5 minutes), and none at 00:05:47 after Dev's 00:00 restart.
    • Switch: I copied one of Main's junctions (and its two splines) onto Dev.
      • The switch display spawns on chunk load.
      • Knocked about 135° off its target yaw, it rotates back in about 35 movement updates and stops exactly on target.
      • Idle, it only gets the 1–3 periodic position syncs per 5 s that Paper sends for every entity, with no yaw change.
      • The same holds after the chunk unloads and reloads (a fresh display entity).
    • No VehicleFramework errors in the Dev log.

Part of the 2026-10-22 ping/lag investigation, together with RPCharacters and Games PRs.

🤖 Generated with Claude Code

The 5-minute vehicle checkpoint committed every vehicle separately on the
main thread, waiting for the disk once per vehicle, then copied the whole
database as a backup. On Main that froze the server for 0.3-0.6 s every
5 minutes. Save all vehicles in one transaction instead, and fall back to
one-by-one saves if that transaction fails so a rollback loses nothing.
The backup copy now runs on the first checkpoint and then every 6th
(30 minutes); the WAL checkpoint still runs every cycle.

Track switches were animated by reading the tags of every rail display
on every tick and re-teleporting each switch even when it was already in
place. Keep the switch displays and their junctions in their own list,
and skip the teleport when the yaw would not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 46ece1e9-7f9a-4eab-92c3-bc2c1467e339
📥 Commits

Reviewing files that changed from the base of the PR and between 7056fcd and 1bcddcc.

📒 Files selected for processing: 6
  • src/main/java/net/tfminecraft/vehicleframework/database/VehiclePersistence.java
  • src/main/java/net/tfminecraft/vehicleframework/managers/VehicleManager.java
  • src/main/java/net/tfminecraft/vehicleframework/tracks/TrackDisplayManager.java
  • src/test/java/net/tfminecraft/vehicleframework/database/DatabaseRuntimeCoverageTest.java
  • src/test/java/net/tfminecraft/vehicleframework/managers/VehicleManagerCoverageTest.java
  • src/test/java/net/tfminecraft/vehicleframework/tracks/TrackDisplayManagerCoverageTest.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

  • Improvements
    • Vehicle data is saved in batches during checkpoints, with individual saves used if batch saving fails. Failed saves are logged, and destroyed vehicles are excluded.
    • Database backups are now created at the first checkpoint and every sixth checkpoint; WAL checkpointing continues on every run.
    • Switch displays update only when their angle changes, and unloaded or invalid displays are cleaned up.

The change adds transactional batch persistence for live vehicles and uses it during checkpoints. It also tracks switch displays separately and updates them through the switch tick and display lifecycle.

Changes: Vehicle persistence checkpoints

Layer / File(s) Summary
Batch persistence and result handling
src/main/java/net/tfminecraft/vehicleframework/database/VehiclePersistence.java, src/test/java/net/tfminecraft/vehicleframework/database/DatabaseRuntimeCoverageTest.java
saveLiveBatch saves vehicles in a transaction when a repository is available. If the transaction fails, it retries individual saves. Tests cover empty input, per-vehicle results, transaction fallback, and unavailable repositories.
Checkpoint save and backup cadence
src/main/java/net/tfminecraft/vehicleframework/managers/VehicleManager.java, src/test/java/net/tfminecraft/vehicleframework/managers/VehicleManagerCoverageTest.java
Checkpoints batch-save non-destroyed vehicles and log failures. WAL checkpointing remains on each run. Backups run on the first checkpoint and every sixth checkpoint. Tests verify these behaviours.

Switch display tracking

Layer / File(s) Summary
Switch index lifecycle and tick updates
src/main/java/net/tfminecraft/vehicleframework/tracks/TrackDisplayManager.java, src/test/java/net/tfminecraft/vehicleframework/tracks/TrackDisplayManagerCoverageTest.java
TrackDisplayManager indexes switch displays with their junction IDs and updates the index during spawning, despawning, and chunk cleanup. Switch ticks use the index and skip yaw updates when the yaw delta is below 1e-4 degrees. Tests cover idle, moving, settled, and unloaded switches.

Priority: ⬇️ Low

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

Change: Refactor

Suggested reviewers: ryanbarlow97

Merge Risk: ⚪ Minimal · up to 1bcdd

The backup schedule reflects the intended performance trade-off. No identified issue needs resolution before merge.

- [ ] 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 23, 2026
@XxFran10xX
XxFran10xX marked this pull request as ready for review October 23, 2026 00:16
@XxFran10xX

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 23, 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
XxFran10xX merged commit f77a54e into main Oct 23, 2026
4 checks passed
@XxFran10xX
XxFran10xX deleted the perf/checkpoint-batch-and-switch-tick branch October 23, 2026 00:22
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