mf_persistence/subscriber/
mod.rs1use std::{collections::HashSet, fmt, sync::Arc};
3
4use async_trait::async_trait;
5use dashmap::DashMap;
6use mf_core::{
7 event::{Event, EventHandler},
8 ForgeResult,
9};
10use mf_state::transaction::{get_tr_id, Transaction};
11use serde_json::json;
12
13use crate::api::{CommitMode, EventStore, PersistOptions, PersistedEvent, Snapshot};
14use crate::ser::{
15 checksum32, compress_if_needed, frame_invert_steps, frame_steps,
16 SnapshotData, TypeWrapper,
17};
18
19#[derive(Default)]
20struct SnapshotCounters {
21 last_snapshot_ms: i64,
22 last_snapshot_upto_lsn: i64,
23 events_since: u32,
24 bytes_since: u64,
25}
26
27#[derive(Copy, Clone)]
28enum PersistAction {
29 Apply,
30 Undo,
31 Redo,
32}
33
34pub struct SnapshotSubscriber<E: EventStore + 'static> {
35 store: Arc<E>,
36 options: PersistOptions,
37 commit_mode: CommitMode,
38 default_doc_id: String,
39 persisted: DashMap<u64, ()>,
41 snap_counters: DashMap<String, SnapshotCounters>,
43 pending_snapshot_lsn: DashMap<String, i64>,
45}
46
47impl<E: EventStore + 'static> fmt::Debug for SnapshotSubscriber<E> {
48 fn fmt(
49 &self,
50 f: &mut fmt::Formatter<'_>,
51 ) -> fmt::Result {
52 f.write_str("SnapshotSubscriber")
53 }
54}
55
56impl<E: EventStore + 'static> Clone for SnapshotSubscriber<E> {
57 fn clone(&self) -> Self {
58 Self {
59 store: self.store.clone(),
60 options: self.options.clone(),
61 commit_mode: self.commit_mode,
62 default_doc_id: self.default_doc_id.clone(),
63 persisted: DashMap::new(),
64 snap_counters: DashMap::new(),
65 pending_snapshot_lsn: DashMap::new(),
66 }
67 }
68}
69
70impl<E: EventStore + 'static> SnapshotSubscriber<E> {
71 pub fn new(
72 store: Arc<E>,
73 options: PersistOptions,
74 default_doc_id: impl Into<String>,
75 ) -> Self {
76 Self {
77 commit_mode: options.commit_mode,
78 store,
79 options,
80 default_doc_id: default_doc_id.into(),
81 persisted: DashMap::new(),
82 snap_counters: DashMap::new(),
83 pending_snapshot_lsn: DashMap::new(),
84 }
85 }
86
87 async fn persist_one(
88 &self,
89 tr_id: u64,
90 doc_id: &str,
91 frames: Vec<TypeWrapper>,
92 meta: serde_json::Value,
93 ) -> ForgeResult<Option<(i64, usize)>> {
94 if frames.is_empty() {
95 return Ok(None);
96 }
97 let framed = serde_json::to_vec(&frames).map_err(|e| {
98 mf_core::error::error_utils::middleware_error(format!(
99 "步骤编码失败: {e}"
100 ))
101 })?;
102 let payload = compress_if_needed(&framed, self.options.compression)
103 .map_err(|e| {
104 mf_core::error::error_utils::middleware_error(e.to_string())
105 })?;
106 let checksum = checksum32(&payload);
107
108 let ev = PersistedEvent {
109 lsn: 0,
110 tr_id,
111 doc_id: doc_id.to_string(),
112 ts: chrono::Utc::now().timestamp_millis(),
113 actor: None,
114 idempotency_key: format!("tr:{tr_id}"),
115 payload,
116 meta,
117 checksum,
118 };
119
120 match self.commit_mode {
121 CommitMode::MemoryOnly => Ok(None),
122 _ => {
123 let bytes = ev.payload.len();
124 self.store
125 .append(ev)
126 .await
127 .map(|lsn| Some((lsn, bytes)))
128 .map_err(|e| {
129 mf_core::error::error_utils::middleware_error(
130 e.to_string(),
131 )
132 })
133 },
134 }
135 }
136
137 fn resolve_doc_id(
138 &self,
139 transaction: &Transaction,
140 state: &mf_state::State,
141 ) -> String {
142 let doc_id = transaction
143 .get_meta::<String>("doc_id")
144 .unwrap_or_else(|| state.doc().root_id().to_string());
145 if doc_id.is_empty() {
146 self.default_doc_id.clone()
147 } else {
148 doc_id
149 }
150 }
151
152 fn should_snapshot(
153 &self,
154 doc_id: &str,
155 ) -> bool {
156 if let Some(counters) = self.snap_counters.get(doc_id) {
157 let now = chrono::Utc::now().timestamp_millis();
158 let time_ok = self.options.snapshot_every_ms > 0
159 && now - counters.last_snapshot_ms
160 >= self.options.snapshot_every_ms as i64;
161 let count_ok = self.options.snapshot_every_n_events > 0
162 && counters.events_since
163 >= self.options.snapshot_every_n_events;
164 let bytes_ok = self.options.snapshot_every_bytes > 0
165 && counters.bytes_since >= self.options.snapshot_every_bytes;
166 time_ok || count_ok || bytes_ok
167 } else {
168 false
169 }
170 }
171
172 async fn write_snapshot(
173 &self,
174 doc_id: &str,
175 upto_lsn: i64,
176 state: &mf_state::State,
177 ) -> ForgeResult<()> {
178 let mut ser = state.serialize().await.map_err(|e| {
179 mf_core::error::error_utils::middleware_error(format!(
180 "状态序列化失败: {e}"
181 ))
182 })?;
183 let snap = SnapshotData {
184 node_pool: std::mem::take(&mut ser.node_pool),
185 state_fields: std::mem::take(&mut ser.state_fields),
186 };
187 let blob = serde_json::to_vec(&snap).map_err(|e| {
188 mf_core::error::error_utils::middleware_error(format!(
189 "快照编码失败: {e}"
190 ))
191 })?;
192 let blob = compress_if_needed(&blob, self.options.compression)
193 .map_err(|e| {
194 mf_core::error::error_utils::middleware_error(format!(
195 "快照压缩失败: {e}"
196 ))
197 })?;
198
199 let snap = Snapshot {
200 doc_id: doc_id.to_string(),
201 upto_lsn,
202 created_at: chrono::Utc::now().timestamp_millis(),
203 state_blob: blob,
204 version: 1,
205 };
206
207 self.store.write_snapshot(snap).await.map_err(|e| {
208 mf_core::error::error_utils::middleware_error(e.to_string())
209 })?;
210
211 let mut entry =
213 self.snap_counters.entry(doc_id.to_string()).or_default();
214 entry.last_snapshot_ms = chrono::Utc::now().timestamp_millis();
215 entry.last_snapshot_upto_lsn = upto_lsn;
216 entry.events_since = 0;
217 entry.bytes_since = 0;
218 Ok(())
219 }
220
221 async fn process_transactions(
222 &self,
223 state: &Arc<mf_state::State>,
224 transactions: &[Arc<Transaction>],
225 action: PersistAction,
226 ) -> ForgeResult<()> {
227 if transactions.is_empty() {
228 return Ok(());
229 }
230 let mut touched_docs: HashSet<String> = HashSet::new();
231
232 for tr in transactions.iter() {
233 let doc_id = self.resolve_doc_id(tr, state);
234 let (persist_id, frames, meta) = match action {
235 PersistAction::Apply => (tr.id, frame_steps(tr), json!({})),
236 PersistAction::Undo => (
237 get_tr_id(),
238 frame_invert_steps(tr),
239 json!({
240 "action": "undo",
241 "source_tr_id": tr.id,
242 }),
243 ),
244 PersistAction::Redo => (
245 get_tr_id(),
246 frame_steps(tr),
247 json!({
248 "action": "redo",
249 "source_tr_id": tr.id,
250 }),
251 ),
252 };
253
254 if frames.is_empty() {
255 continue;
256 }
257
258 if matches!(action, PersistAction::Apply)
259 && self.persisted.contains_key(&persist_id)
260 {
261 continue;
262 }
263
264 match self.persist_one(persist_id, &doc_id, frames, meta).await {
265 Ok(Some((lsn, bytes))) => {
266 self.persisted.insert(persist_id, ());
267 touched_docs.insert(doc_id.clone());
268 let mut entry =
269 self.snap_counters.entry(doc_id.clone()).or_default();
270 entry.events_since += 1;
271 entry.bytes_since += bytes as u64;
272 let cur = self
273 .pending_snapshot_lsn
274 .get(&doc_id)
275 .map(|v| *v.value())
276 .unwrap_or(-1);
277 if lsn > cur {
278 self.pending_snapshot_lsn
279 .insert(doc_id.clone(), lsn);
280 }
281 },
282 Ok(None) => {},
283 Err(e) => return Err(e),
284 }
285 }
286
287 self.flush_snapshots(touched_docs, state).await
288 }
289
290 async fn flush_snapshots(
291 &self,
292 touched_docs: HashSet<String>,
293 state: &Arc<mf_state::State>,
294 ) -> ForgeResult<()> {
295 for doc_id in touched_docs.into_iter() {
296 if let Some(upto) =
297 self.pending_snapshot_lsn.get(&doc_id).map(|v| *v.value())
298 {
299 if upto >= 0 {
300 let has_snapshot = self
301 .store
302 .latest_snapshot(&doc_id)
303 .await
304 .map(|s| s.is_some())
305 .unwrap_or(false);
306 if !has_snapshot || self.should_snapshot(&doc_id) {
307 self.write_snapshot(&doc_id, upto, state).await?;
308 self.pending_snapshot_lsn.remove(&doc_id);
309 }
310 }
311 }
312 }
313 Ok(())
314 }
315}
316
317#[async_trait]
318impl<E: EventStore + 'static> EventHandler<Event> for SnapshotSubscriber<E> {
319 async fn handle(
320 &self,
321 event: &Event,
322 ) -> ForgeResult<()> {
323 match event {
324 Event::Create(state) => {
325 let doc_id = state.doc().root_id().to_string();
326 let has_snapshot = self
327 .store
328 .latest_snapshot(&doc_id)
329 .await
330 .map(|s| s.is_some())
331 .unwrap_or(false);
332 if !has_snapshot {
333 self.write_snapshot(&doc_id, 0, state).await?;
334 }
335 },
336 Event::TrApply { new_state, transactions, .. } => {
337 self.process_transactions(new_state, transactions, PersistAction::Apply).await?;
338 },
339 Event::Undo { new_state, transactions, .. } => {
340 self.process_transactions(new_state, transactions, PersistAction::Undo).await?;
341 },
342 Event::Redo { new_state, transactions, .. } => {
343 self.process_transactions(new_state, transactions, PersistAction::Redo).await?;
344 },
345 _ => {},
346 }
347 Ok(())
348 }
349}