forked from SO4-Markets/so4-oracle
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstate.rs
More file actions
206 lines (180 loc) · 5.9 KB
/
Copy pathstate.rs
File metadata and controls
206 lines (180 loc) · 5.9 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
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::sync::Arc;
use std::time::SystemTime;
use serde::Serialize;
use tokio::sync::{Mutex, RwLock};
use tokio_util::sync::CancellationToken;
use crate::config::Config;
use crate::metrics::Metrics;
pub const FAILURE_RING_CAPACITY: usize = 256;
#[derive(Debug, Clone, Serialize)]
pub struct CachedPrice {
pub token_address: String,
pub symbol: String,
pub display_symbol: String,
pub keeper_index: u32,
#[serde(serialize_with = "ser_i128_str")]
pub min: i128,
#[serde(serialize_with = "ser_i128_str")]
pub max: i128,
#[serde(serialize_with = "ser_i128_str")]
pub median: i128,
pub timestamp: u64,
#[serde(rename = "ledger")]
pub ledger_seq: u32,
pub sources_used: Vec<String>,
pub signature: String,
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct PriceCache {
pub prices: BTreeMap<String, CachedPrice>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated: Option<SystemTime>,
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct CycleStatus {
pub price_cycle_running: bool,
pub keeper_cycle_running: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_price_cycle_at: Option<SystemTime>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_keeper_cycle_at: Option<SystemTime>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_keeper_cycle_latency_ms: Option<u64>,
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct KeeperStatus {
pub pending_orders: usize,
pub pending_deposits: usize,
pub pending_withdrawals: usize,
pub last_executions: Vec<KeeperExecution>,
}
#[derive(Debug, Clone, Serialize)]
pub struct KeeperExecution {
pub timestamp: SystemTime,
pub operation: String,
pub key: String,
pub tx_hash: Option<String>,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct FailedSubmission {
pub at: SystemTime,
pub operation: String,
pub network: String,
pub token: String,
pub symbol: String,
#[serde(serialize_with = "ser_i128_str")]
pub min: i128,
#[serde(serialize_with = "ser_i128_str")]
pub max: i128,
#[serde(skip_serializing_if = "Option::is_none")]
pub tx_hash: Option<String>,
pub error: String,
pub timestamp: u64,
pub ledger_seq: u32,
}
pub fn ser_i128_str<S>(value: &i128, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&value.to_string())
}
#[derive(Debug, Clone)]
pub struct RingBuffer<T> {
capacity: usize,
items: VecDeque<T>,
}
impl<T> RingBuffer<T> {
pub fn new(capacity: usize) -> Self {
Self {
capacity,
items: VecDeque::with_capacity(capacity),
}
}
pub fn push(&mut self, item: T) {
if self.capacity == 0 {
return;
}
while self.items.len() >= self.capacity {
self.items.pop_front();
}
self.items.push_back(item);
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &T> {
self.items.iter()
}
}
impl<T> Default for RingBuffer<T> {
fn default() -> Self {
Self::new(FAILURE_RING_CAPACITY)
}
}
#[derive(Debug, Default, Clone)]
pub struct ReadyCache {
pub last_checked: Option<std::time::Instant>,
pub last_error: Option<(axum::http::StatusCode, String)>,
}
/// Number of consecutive `freeze_order` failures before a key is permanently
/// skipped and a loud alert is emitted (#498).
pub const MAX_CONSECUTIVE_FREEZE_FAILURES: u32 = 3;
#[derive(Clone)]
pub struct AppState {
pub config: Arc<Config>,
pub http: reqwest::Client,
pub price_cache: Arc<RwLock<PriceCache>>,
pub cycle_status: Arc<RwLock<CycleStatus>>,
pub failures: Arc<Mutex<RingBuffer<FailedSubmission>>>,
pub keeper_status: Arc<RwLock<KeeperStatus>>,
pub in_flight_keys: Arc<Mutex<std::collections::HashSet<String>>>,
pub ready_cache: Arc<RwLock<ReadyCache>>,
pub metrics: Arc<Metrics>,
pub shutdown_token: CancellationToken,
/// Per-order-key count of consecutive `freeze_order` failures.
/// Once a key reaches MAX_CONSECUTIVE_FREEZE_FAILURES it is added to
/// `frozen_order_blacklist` and never retried again (#498).
pub freeze_failure_counts: Arc<Mutex<HashMap<String, u32>>>,
/// Order keys that have been permanently abandoned after too many
/// consecutive freeze failures (#498).
pub frozen_order_blacklist: Arc<Mutex<HashMap<String, u32>>>,
}
impl AppState {
pub fn new(config: Arc<Config>) -> Self {
Self {
config,
http: crate::http::client().clone(),
price_cache: Arc::new(RwLock::new(PriceCache::default())),
cycle_status: Arc::new(RwLock::new(CycleStatus::default())),
failures: Arc::new(Mutex::new(RingBuffer::default())),
keeper_status: Arc::new(RwLock::new(KeeperStatus::default())),
in_flight_keys: Arc::new(Mutex::new(std::collections::HashSet::new())),
ready_cache: Arc::new(RwLock::new(ReadyCache::default())),
metrics: Metrics::new(),
shutdown_token: CancellationToken::new(),
freeze_failure_counts: Arc::new(Mutex::new(HashMap::new())),
frozen_order_blacklist: Arc::new(Mutex::new(HashMap::new())),
}
}
}
#[cfg(test)]
mod tests {
use super::RingBuffer;
#[test]
fn ring_buffer_evicts_oldest_items_at_capacity() {
let mut buffer = RingBuffer::new(2);
buffer.push(1);
buffer.push(2);
buffer.push(3);
let items = buffer.iter().copied().collect::<Vec<_>>();
assert_eq!(items, vec![2, 3]);
}
#[test]
fn ring_buffer_with_zero_capacity_never_grows() {
let mut buffer = RingBuffer::new(0);
buffer.push(1);
buffer.push(2);
buffer.push(3);
assert_eq!(buffer.iter().count(), 0);
}
}