Persistence Architecture
Hyperscape implements a robust persistence layer with write-ahead logging, transactional saves, and crash recovery to prevent data loss during server crashes or network failures.Persistence code lives in
packages/server/src/persistence/ and packages/shared/src/systems/shared/character/.Architecture Overview
The persistence system uses multiple layers of protection:- Transactional Saves — Equipment and bank operations use database transactions
- Immediate Persistence — Critical operations (item pickup/drop) save immediately
- Auto-Save — Periodic saves every 5 seconds for inventory/equipment
- Write-Ahead Logging — Phase 2 scaffolding for trade/bank crash recovery
- Unified Payloads — Single source of truth for player data loading
Transactional Saves
Equipment Persistence
Equipment saves use database transactions to prevent data loss during crashes:Atomicity Guarantee: Either both delete and insert succeed, or both are rolled back. No partial state is possible.
Bank Persistence
Bank saves also use transactions for atomic updates:Auto-Save System
Reduced Save Intervals
Auto-save intervals were reduced from 30 seconds to 5 seconds to minimize data loss:- Before: Up to 30 seconds of progress could be lost on crash
- After: Maximum 5 seconds of progress lost
- Performance: Negligible overhead (6x more frequent saves with minimal DB load)
Immediate Persistence
Critical operations persist immediately to prevent duplication or loss:Item Pickup
Item Drop
Why Immediate? If the server crashes after an item is picked up but before the next auto-save, the item could be lost. Immediate persistence ensures the database is updated before the operation completes.
Unified PLAYER_JOINED Payload
Single Source of Truth Pattern
ThePLAYER_JOINED event now includes equipment and inventory data loaded from the database before the event is emitted. This eliminates race conditions where multiple systems query the database independently.
System Integration
Systems now load from the event payload instead of querying the database:Backwards Compatibility: If the payload doesn’t include equipment/inventory data (old server version), systems fall back to querying the database directly.
EventBus Async Handler Tracking
The EventBus now tracks pending async handlers to enable graceful shutdown:Graceful Shutdown: Call
eventBus.waitForPendingHandlers() before server shutdown to ensure all database saves complete.Write-Ahead Logging (Phase 2)
PersistenceService
ThePersistenceService provides write-ahead logging for critical operations. Status: Phase 2 scaffolding — not yet integrated into game systems.
Operations Log Table
Theoperations_log table stores write-ahead log entries:
trade_complete— Trade finalizationbank_deposit— Bank deposit transactionbank_withdraw— Bank withdrawal transactioninventory_add— Inventory item additioninventory_remove— Inventory item removalequipment_change— Equipment slot change
WAL Pattern
The write-ahead logging pattern ensures durability:Auto-Save Configuration
Equipment System
Inventory System
Performance: 5-second auto-save has negligible overhead. Database writes are batched and use transactions for efficiency.
Immediate Persistence
When to Persist Immediately
Immediate persistence is used for operations that could cause duplication or loss:Implementation
Database Schema
Operations Log
Magic Skill Columns
Magic skill support was added in migration0027_messy_dorian_gray.sql:
magicLevel— Magic skill level (default: 1)magicXp— Magic skill XP (default: 0)selectedSpell— Autocast spell ID (null = no autocast)
Crash Recovery
Equipment Loading
Equipment is loaded from the database during character selection and passed via thePLAYER_JOINED event:
Race Condition Fix: Previously, both
character-selection.ts and EquipmentSystem would query the database independently, causing race conditions. Now, character-selection loads the data once and passes it via the event payload.Inventory Loading
Inventory follows the same pattern:Performance Considerations
Batched Writes
The PersistenceService uses batched writes for performance:- Latency: Maximum 50ms delay before write
- Throughput: Up to 100 operations per batch
- Durability: Operations are logged immediately (durability point), batching only affects completion marking
Cleanup
Old completed operations are cleaned up periodically:Recommendation: Run cleanup daily via cron job or scheduled task to prevent table growth.