forked from Echo-Mirror-Butler/echomirror-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcursor.rs
More file actions
84 lines (75 loc) · 2.61 KB
/
Copy pathcursor.rs
File metadata and controls
84 lines (75 loc) · 2.61 KB
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
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
/// Resumable cursor for Stellar ledger sync.
/// Persist this between process restarts to avoid re-scanning the entire chain.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncCursor {
/// The last fully processed ledger sequence number
pub ledger_sequence: u32,
/// Horizon paging token — use this as `cursor=` on the next request
pub paging_token: String,
/// When this cursor was last updated
pub last_synced_at: DateTime<Utc>,
/// Number of transactions processed since sync started
pub total_processed: u64,
}
impl SyncCursor {
pub fn genesis() -> Self {
Self {
ledger_sequence: 0,
paging_token: "now".to_string(),
last_synced_at: Utc::now(),
total_processed: 0,
}
}
pub fn from_ledger(sequence: u32) -> Self {
Self {
ledger_sequence: sequence,
paging_token: sequence.to_string(),
last_synced_at: Utc::now(),
total_processed: 0,
}
}
}
/// Trait for persisting sync cursors — implement this to store cursor in
/// a database, Redis, file, or any other backend.
///
/// Store failures should be returned as `EchoMirrorError::Sync`. The engine
/// treats a failed `load` as fatal for the current attempt (it retries with
/// backoff) and counts failed `save`s in metrics without stopping the stream.
#[async_trait::async_trait]
pub trait CursorStore: Send + Sync {
async fn load(&self, account: &str) -> echomirror_core::Result<Option<SyncCursor>>;
async fn save(&self, account: &str, cursor: &SyncCursor) -> echomirror_core::Result<()>;
}
/// In-memory cursor store — suitable for development and single-process use.
pub struct InMemoryCursorStore {
cursors: Arc<RwLock<std::collections::HashMap<String, SyncCursor>>>,
}
impl InMemoryCursorStore {
pub fn new() -> Self {
Self {
cursors: Arc::new(RwLock::new(std::collections::HashMap::new())),
}
}
}
impl Default for InMemoryCursorStore {
fn default() -> Self {
Self::new()
}
}
#[async_trait::async_trait]
impl CursorStore for InMemoryCursorStore {
async fn load(&self, account: &str) -> echomirror_core::Result<Option<SyncCursor>> {
Ok(self.cursors.read().await.get(account).cloned())
}
async fn save(&self, account: &str, cursor: &SyncCursor) -> echomirror_core::Result<()> {
self.cursors
.write()
.await
.insert(account.to_string(), cursor.clone());
Ok(())
}
}