Understand how Multisynq automatically saves and restores session state for persistence and new user synchronization
Snapshots are automatic copies of the model state that Multisynq saves to the cloud. This system provides seamless persistence and enables new users to join existing sessions efficiently without replaying the entire event history.
Automatic save functionality for your applicationWhen users quit or reload:
Session state is preserved in the latest snapshot
When they return, the exact state is restored
No progress is lost
Copy
Ask AI
// Example: Game state preservationclass GameModel extends Multisynq.Model { init() { this.level = 1; this.score = 0; this.playerPositions = new Map(); } // All of this state is automatically saved in snapshots // and restored when the session resumes}
Efficient joining of existing sessionsWhen a new user joins an active session:
The local model is initialized with data from the last snapshot
Copy
Ask AI
// Instead of calling init(), your model is restored from snapshot// This bypasses normal initializationclass GameModel extends Multisynq.Model { init() { // This WON'T be called for users joining from snapshot console.log("Fresh session start"); }}
Your model’s init() method is NOT called when loading from a snapshot.
2. Event Replay
The reflector resends all events transmitted after the snapshot
Copy
Ask AI
// All events since the snapshot are replayed// This brings the snapshot up to the current state// Events are processed in the exact same order
Event replay is deterministic - the same events produce the same results.
3. Model Simulation
The model simulates all events to bring the snapshot up-to-date
Copy
Ask AI
// Your model processes all missed events// This happens automatically and synchronously// The model becomes current with other users
4. View Initialization
The local view initializes to match the model state
Copy
Ask AI
class GameView extends Multisynq.View { init() { // This IS called for snapshot joins // Your view should be prepared to handle existing state this.canvas = document.getElementById('canvas'); // The model may already have data from the snapshot this.displayExistingPlayers(); } displayExistingPlayers() { // Handle the case where players already exist for (const player of this.model.players.values()) { this.createPlayerVisual(player); } }}
Important: When writing your View initialization, account for the fact that the Model may have been restored from a snapshot and already contains data.
❌ Problematic Approach
✅ Correct Approach
Copy
Ask AI
class GameView extends Multisynq.View { init() { this.players = new Map(); // This assumes the model starts empty // But it might already have players from a snapshot! this.subscribe("player", "joined", this.addPlayer); } addPlayer(player) { this.players.set(player.id, new PlayerVisual(player)); } // Missing: handling existing players from snapshot}
Copy
Ask AI
class GameView extends Multisynq.View { init() { this.players = new Map(); // Handle existing players from snapshot this.initializeExistingPlayers(); // Subscribe to future player events this.subscribe("player", "joined", this.addPlayer); this.subscribe("player", "left", this.removePlayer); } initializeExistingPlayers() { // Check if model already has players (from snapshot) for (const player of this.model.players.values()) { this.createPlayerVisual(player); } } createPlayerVisual(player) { const visual = new PlayerVisual(player); this.players.set(player.id, visual); } addPlayer(player) { this.createPlayerVisual(player); } removePlayer(playerId) { const visual = this.players.get(playerId); if (visual) { visual.destroy(); this.players.delete(playerId); } }}
Current Limitation: The snapshot system is currently unoptimized and may cause performance hitches when snapshots are taken.
🐌 Current Behavior
🔄 Mitigation Strategies
Performance hitch: Brief pause when snapshot is taken
Visible to users: May notice application freeze
Depends on model size: Larger models = longer pause
Copy
Ask AI
// During snapshot creation:// 1. Model state is serialized// 2. Data is compressed// 3. Snapshot is transmitted// 4. Normal execution resumes
Until the system is optimized, you can:
Keep models lean: Avoid storing unnecessary data
Use efficient data structures: Prefer simple objects over complex ones
Clean up regularly: Remove unused objects
Monitor model size: Be aware of your state footprint
Copy
Ask AI
class OptimizedModel extends Multisynq.Model { init() { // Use efficient data structures this.players = new Map(); // Better than array for lookups this.cleanup(); } cleanup() { // Regularly clean up unused data this.future(10000).cleanup(); // Remove expired objects for (const [id, player] of this.players) { if (player.isExpired()) { this.players.delete(id); } } }}
class GameModel extends Multisynq.Model { init() { // This only runs for fresh sessions this.fromSnapshot = false; console.log("Fresh session start"); } // Called after snapshot restore OR fresh init start() { if (!this.fromSnapshot) { this.fromSnapshot = true; console.log("Loaded from snapshot"); } }}
Verify snapshot state is correct
Copy
Ask AI
class GameModel extends Multisynq.Model { validateState() { // Check state integrity after snapshot load console.log("Players:", this.players.size); console.log("Game state:", this.gameState); console.log("Score:", this.score); // Verify relationships for (const player of this.players.values()) { if (!player.isValid()) { console.error("Invalid player state:", player); } } }}
class GameModel extends Multisynq.Model { init() { // Fresh game start this.level = 1; this.score = 0; this.players = new Map(); this.gameState = "waiting"; this.startTime = Date.now(); } // All this state is automatically preserved // When users rejoin, they continue exactly where they left off}class GameView extends Multisynq.View { init() { // Handle both fresh starts and snapshot resumes this.initializeUI(); this.displayCurrentState(); } displayCurrentState() { // Show current level, score, etc. this.updateScore(this.model.score); this.updateLevel(this.model.level); // Display existing players for (const player of this.model.players.values()) { this.createPlayerDisplay(player); } }}
💬 Chat History
Preserving conversation history
Copy
Ask AI
class ChatModel extends Multisynq.Model { init() { this.messages = []; this.users = new Map(); } addMessage(message) { this.messages.push(message); // Keep only last 100 messages for snapshot efficiency if (this.messages.length > 100) { this.messages.shift(); } }}class ChatView extends Multisynq.View { init() { this.chatContainer = document.getElementById('chat'); // Display existing messages from snapshot this.displayMessageHistory(); // Subscribe to new messages this.subscribe("chat", "message", this.displayMessage); } displayMessageHistory() { for (const message of this.model.messages) { this.displayMessage(message); } }}
Snapshots are fundamental to Multisynq’s persistence and scalability. Understanding how they work will help you design better models and views that handle session continuity seamlessly.