1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Actor persistence capabilities for state snapshots and recovery.
//!
//! This module enables actors to persist their state and recover from failures
//! or system restarts. It provides a flexible architecture that supports
//! multiple storage backends and automatic snapshot management.
//!
//! # Core Concepts
//!
//! ## Snapshots
//!
//! A **snapshot** is a serializable representation of an actor's state that can be
//! captured from the Actor's current state and supports serde for restoration.
//! Snapshots are one of the ActorArgs, which means they can be used to initialize
//! actors during recovery. Snapshots must:
//! - Capture all essential state needed to restore the actor
//! - Be serializable (implement `Serialize` + `Deserialize`)
//! - Be usable as actor initialization arguments (implement `ActorArgs`)
//! - Convert from the actor's current state (implement `From<&Actor>`)
//!
//! ## Storage Backends
//!
//! Storage backends implement the `PersistentStorage` trait and handle:
//! - Reading snapshots by actor ID
//! - Writing snapshots atomically
//! - Storage-specific optimizations (compression, indexing, etc.)
//!
//! Built-in storage backends:
//! - **File system**: Local disk storage with configurable directories
//!
//! ## Persistence Lifecycle
//!
//! 1. **Snapshot Creation**: Actors create snapshots from their current state
//! 2. **Storage**: Snapshots are written to the configured storage backend
//! 3. **Recovery**: Actors are restored from snapshots during spawn operations
//! 4. **Automatic Management**: Framework handles snapshot timing and cleanup
//!
//! # Usage Patterns
//!
//! ## Basic Persistence
//!
//! ```no_run
//! use theta::prelude::*;
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Debug, Clone, Serialize, Deserialize, ActorArgs)]
//! struct Counter {
//! value: i64,
//! name: String,
//! }
//!
//! # #[derive(Debug, Clone, Serialize, Deserialize)]
//! # struct Increment(i64);
//! # struct DummyStorage;
//! # impl PersistentStorage for DummyStorage {
//! # async fn try_read(&self, _: ActorId) -> Result<Vec<u8>, anyhow::Error> { Ok(vec![]) }
//! # async fn try_write(&self, _: ActorId, _: Vec<u8>) -> Result<(), anyhow::Error> { Ok(()) }
//! # }
//! # static STORAGE: DummyStorage = DummyStorage;
//!
//! // The `snapshot` flag automatically implements PersistentActor
//! // with Snapshot = Counter, RuntimeArgs = (), ActorArgs = Counter
//! #[actor("12345678-1234-5678-9abc-123456789abc", snapshot)]
//! impl Actor for Counter {
//! const _: () = {
//! async |Increment(amount): Increment| {
//! self.value += amount;
//! // Manual snapshot saving when needed
//! let _ = ctx.save_snapshot(&STORAGE, self).await;
//! };
//! };
//! }
//! ```
//!
//! ## Custom Snapshot Types
//!
//! ```
//! # use theta::prelude::*;
//! # use serde::{Serialize, Deserialize};
//! # use std::collections::HashMap;
//! # #[derive(Debug, Clone, Serialize, Deserialize, ActorArgs)]
//! # struct Counter { value: i64, cache: HashMap<String, String> }
//! # #[derive(Debug, Clone, Serialize, Deserialize)]
//! # struct Increment(i64);
//! # #[actor("aaaaaaaa-bbbb-cccc-dddd-999999999999", snapshot)]
//! # impl Actor for Counter {
//! # const _: () = { async |Increment(_): Increment| {}; };
//! # }
//! #[derive(Debug, Clone, Serialize, Deserialize)]
//! struct CounterSnapshot {
//! value: i64,
//! // Exclude non-essential state like caches
//! }
//!
//! impl From<&Counter> for CounterSnapshot {
//! fn from(counter: &Counter) -> Self {
//! Self { value: counter.value }
//! }
//! }
//!
//! impl ActorArgs for CounterSnapshot {
//! type Actor = Counter;
//!
//! async fn initialize(_ctx: Context<Counter>, args: &Self) -> Counter {
//! Counter {
//! value: args.value,
//! cache: HashMap::new(), // Rebuilt on recovery
//! }
//! }
//! }
//! ```
//!
//! ## Recovery-aware Spawning
//!
//! ```no_run
//! # use theta::prelude::*;
//! # use serde::{Serialize, Deserialize};
//! # #[derive(Debug, Clone, Serialize, Deserialize, ActorArgs)]
//! # struct Counter { value: i64, name: String }
//! # #[derive(Debug, Clone, Serialize, Deserialize)]
//! # struct Increment(i64);
//! # #[actor("aaaaaaaa-bbbb-cccc-dddd-aaaaaaaaaaaa", snapshot)]
//! # impl Actor for Counter {
//! # const _: () = { async |Increment(_): Increment| {}; };
//! # }
//! # struct DummyStorage;
//! # impl PersistentStorage for DummyStorage {
//! # async fn try_read(&self, _: ActorId) -> Result<Vec<u8>, anyhow::Error> { Ok(vec![]) }
//! # async fn try_write(&self, _: ActorId, _: Vec<u8>) -> Result<(), anyhow::Error> { Ok(()) }
//! # }
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! # let ctx = RootContext::default();
//! # let storage = DummyStorage;
//! # let actor_id = ActorId::new_v4();
//! // Try to restore from snapshot, fallback to new instance
//! let counter = ctx.respawn_or(
//! &storage,
//! actor_id,
//! || (), // runtime args
//! || Counter { value: 0, name: "default".to_string() }
//! ).await?;
//! # Ok(())
//! # }
//! ```
// Re-exports
pub use ;