1use std::collections::{BTreeMap, HashSet};
2use std::sync::atomic::{AtomicUsize, Ordering};
3
4use crate::actor::{Actor, ActorContext};
5use crate::message::{Get, Message, Put};
6use crate::types::*;
7use crate::Config;
8
9use async_trait::async_trait;
10use log::{debug, error, info};
11use serde::{Deserialize, Serialize};
12use sled;
13use tokio::time::{sleep, Duration};
14
15static COUNTER: AtomicUsize = AtomicUsize::new(0);
16
17macro_rules! unwrap_or_return {
18 ( $e:expr ) => {
19 match $e {
20 Ok(x) => x,
21 Err(e) => {
22 error!("{:?}", e);
23 return;
24 }
25 }
26 };
27}
28
29#[derive(Serialize, Deserialize, Debug)]
30struct NodesByPriorityEntry {
31 pub node_id: String,
32 pub child_key: String,
33 pub size: u64,
34}
35
36#[derive(Serialize, Deserialize, Debug)]
38struct SledNodeData {
39 pub id: u64,
40 pub node_id: String,
41 pub child_key: String,
42 pub data: NodeData,
43 pub size: u64,
44 pub priority: i32,
45 pub last_opened: u64,
46 pub times_opened: u32,
47}
48
49impl SledNodeData {
50 pub fn new(node_id: String, child_key: String, data: NodeData, store: &SledStorage) -> Self {
51 let mut s = SledNodeData {
52 id: COUNTER.fetch_add(1, Ordering::Relaxed) as u64,
53 node_id,
54 child_key,
55 data,
56 size: 0,
57 priority: 0,
58 last_opened: 0,
59 times_opened: 0,
60 };
61 s.update_size();
62 s.update_priority(store, true);
63 s
64 }
65
66 fn update_size(&mut self) {
67 let size = (self.node_id.len() + self.child_key.len() + self.data.value.size()) as u64;
68 self.size = size;
69 }
70
71 pub fn update_priority(&mut self, storage: &SledStorage, force_index: bool) {
72 let old_prio = self.priority;
74 let mut changed = false;
75 if let Some(first_letter) = self.node_id.chars().next() {
76 if (first_letter == '~') || (self.node_id == "#") {
77 let base_prio = match storage.config.my_pub {
79 Some(ref my_pub) => {
80 match self.node_id.find(my_pub) {
81 Some(1) => 110, _ => 100,
83 }
84 }
85 _ => 100,
86 };
87 self.priority = base_prio
88 + ((1000000.0 / (self.size as f64)).log10().max(0.1)
89 * ((self.times_opened + 10) as f64).log10()) as i32;
90 debug!(
91 "{}.{} new priority {}",
92 self.node_id, self.child_key, self.priority
93 );
94 changed = true;
95 }
96 }
97 if !changed {
98 self.priority = ((1000000.0 / (self.size as f64)).log10().max(0.1)
99 * ((self.times_opened + 10) as f64).log10())
100 .min(99.0) as i32;
101 debug!(
102 "{}.{} new priority {}",
103 self.node_id, self.child_key, self.priority
104 );
105 }
106 if force_index || (self.priority != old_prio) {
107 let index = storage.get_index();
109 let old_index_key = format!("{:0>9}{}", old_prio, self.id);
110 index
111 .remove(&old_index_key)
112 .expect("failed to remove old index key");
113 let new_index_key = format!("{:0>9}{}", self.priority, self.id);
114 debug!("old {} new {}", old_index_key, new_index_key);
115 let entry = NodesByPriorityEntry {
116 node_id: self.node_id.clone(),
117 child_key: self.child_key.clone(),
118 size: self.size,
119 };
120 index
121 .insert(new_index_key, bincode::serialize(&entry).unwrap())
122 .expect("failed to write into node priority index");
123 }
124 }
125}
126
127#[derive(Clone)]
128pub struct SledStorage {
129 store: sled::Db,
130 meta: sled::Tree,
131 max_size: Option<u64>,
132 pub config: Config,
133}
134
135impl SledStorage {
136 pub fn new() -> Self {
137 Self::new_with_config(
138 Config::default(),
139 sled::Config::default().path("sled_db"),
140 None,
141 )
142 }
143
144 pub fn new_with_config(
145 config: Config,
146 sled_config: sled::Config,
147 max_size: Option<u64>,
148 ) -> Self {
149 let store = sled_config.open().unwrap();
150 let meta = store.open_tree("_meta").unwrap();
151 meta.set_merge_operator(Self::merge_meta);
152 let s = SledStorage {
153 config,
154 store,
155 meta,
156 max_size,
157 };
158 s.change_size(0);
159 s
160 }
161
162 fn merge_meta(key: &[u8], old_value: Option<&[u8]>, new_value: &[u8]) -> Option<Vec<u8>> {
163 let key = std::str::from_utf8(key).unwrap();
164 if key == "size" {
165 let new_size: u64;
166 let new_value = bincode::deserialize::<i64>(new_value).unwrap();
167 if let Some(old_value) = old_value {
168 let old_value = bincode::deserialize::<u64>(old_value).unwrap();
169 new_size = ((old_value as i64) + new_value).max(0) as u64;
170 } else {
171 new_size = new_value.max(0) as u64;
172 }
173 return Some(bincode::serialize(&new_size).unwrap());
174 }
175 Some(new_value.to_vec())
176 }
177
178 pub fn get_size(&self) -> Result<u64, &'static str> {
179 match self.meta.get(b"size") {
180 Ok(size) => {
181 let size = size.ok_or("no size found")?;
182 bincode::deserialize::<u64>(&size).or(Err("could not deserialize db size"))
183 }
184 _ => Err("sled read failed"),
185 }
186 }
187
188 fn change_size(&self, change: i64) {
189 self.meta
190 .merge(b"size", bincode::serialize(&change).unwrap())
191 .expect("sled db size update failed");
192 }
193
194 fn open_child(
195 &self,
196 node_id: &String,
197 child_key: &String,
198 bytes: &[u8],
199 update_times_opened: bool,
200 ) -> Result<SledNodeData, ()> {
201 let mut res = match bincode::deserialize::<SledNodeData>(bytes) {
202 Ok(v) => Ok(v),
203 _ => {
204 match bincode::deserialize::<NodeData>(bytes) {
206 Ok(v) => Ok(SledNodeData::new(
207 node_id.clone(),
208 child_key.clone(),
209 v,
210 &self,
211 )), _ => {
213 error!("could not deserialize data from sled");
214 Err(())
215 }
216 }
217 }
218 };
219 if update_times_opened {
220 match res {
221 Ok(ref mut node) => {
222 node.times_opened += 1;
223 node.update_priority(&self, false);
224 }
225 _ => {}
226 }
227 }
228 res
229 }
230
231 fn get_index(&self) -> sled::Tree {
232 self.store.open_tree("_nodes_by_priority").unwrap()
234 }
235
236 fn handle_get(&self, get: Get, ctx: &ActorContext) {
237 let res = unwrap_or_return!(self.store.get(&get.node_id.clone()));
238 if res.is_none() {
239 debug!("have not {}", get.node_id);
240 return;
241 }
242 let children = res.unwrap();
243 debug!("have {}: {:?}", get.node_id, children);
244 let children = unwrap_or_return!(self.store.open_tree(&get.node_id));
245 let reply_with_children = match &get.child_key {
246 Some(child_key) => {
247 let child_val = unwrap_or_return!(children.get(child_key));
249 if child_val.is_none() {
250 debug!("requested child {} not found", child_key);
251 return;
252 }
253 debug!("requested child {} was found", child_key);
254 let child_val = unwrap_or_return!(self.open_child(
255 &get.node_id,
256 child_key,
257 &child_val.unwrap(),
258 true
259 ));
260 let _ =
261 children.insert(get.node_id.clone(), bincode::serialize(&child_val).unwrap());
262 let mut r = BTreeMap::new();
263 r.insert(child_key.clone(), child_val.data);
264 r
265 }
266 None => {
267 let mut r = BTreeMap::new();
269 for child in children.iter() {
270 let (k, v) = unwrap_or_return!(child);
271 let k = unwrap_or_return!(std::str::from_utf8(&k)).to_string();
272 let v: SledNodeData =
273 unwrap_or_return!(self.open_child(&get.node_id, &k, &v, true));
274 let _ = children.insert(get.node_id.clone(), bincode::serialize(&v).unwrap());
275 r.insert(k, v.data);
276 }
277 r
278 }
279 };
280 let mut reply_with_nodes = BTreeMap::new();
281 reply_with_nodes.insert(get.node_id.clone(), reply_with_children);
282 let mut recipients = HashSet::new();
283 recipients.insert(get.from.clone());
284 debug!("direct replying to {}", get.from);
285 let my_addr = ctx.addr.clone();
286 let mut put = Put::new(reply_with_nodes, Some(get.id.clone()), my_addr);
287 put.to_string();
288 if put.checksum != get.checksum {
289 let _ = get.from.send(Message::Put(put));
290 } else {
291 debug!("put.checksum == get.checksum, not replying with the same data");
292 }
293 }
294
295 fn handle_put(&self, put: Put) {
296 for (node_id, update_data) in put.updated_nodes.into_iter().rev() {
297 if (node_id.len() > 0) && node_id.chars().next().unwrap() == '_' {
298 continue; }
300 debug!("saving k-v {}: {:?}", node_id, update_data);
301 let res = unwrap_or_return!(self.store.get(&node_id));
302 if let Some(_children) = res {
303 let children = unwrap_or_return!(self.store.open_tree(&node_id));
304 for (child_id, child_data) in update_data {
305 if let Some(existing) = unwrap_or_return!(children.get(&child_id)) {
306 let mut existing: SledNodeData = unwrap_or_return!(
307 self.open_child(&node_id, &child_id, &existing, false)
308 );
309 if child_data.updated_at >= existing.data.updated_at {
310 existing.data = child_data;
311 let old_size = existing.size as i64;
312 existing.update_size();
313 existing.update_priority(&self, false);
314 let _ = children
315 .insert(child_id.clone(), bincode::serialize(&existing).unwrap());
316 self.change_size((existing.size as i64) - old_size);
317 }
318 } else {
319 let child =
320 SledNodeData::new(node_id.clone(), child_id.clone(), child_data, &self);
321 let _ = children.insert(child_id, bincode::serialize(&child).unwrap());
322 self.change_size(child.size as i64);
323 }
324 }
325 } else {
326 let _ = self.store.insert(node_id.clone(), vec![1]);
327 let children = unwrap_or_return!(self.store.open_tree(&node_id));
328 for (child_id, child_data) in update_data {
329 let child =
330 SledNodeData::new(node_id.clone(), child_id.clone(), child_data, &self);
331 let _ = children.insert(child_id, bincode::serialize(&child).unwrap());
332 self.change_size(child.size as i64);
333 }
334 }
335 }
336 }
337
338 fn evict(&self, amount: u64) {
339 let mut evicted = 0;
340 let index = self.store.open_tree("_nodes_by_priority").unwrap();
341 while evicted < amount {
342 match index.pop_min() {
343 Ok(opt) => {
345 if let Some((_key, entry)) = opt {
346 let entry = bincode::deserialize::<NodesByPriorityEntry>(&entry).unwrap();
347 let node = unwrap_or_return!(self.store.open_tree(&entry.node_id));
348 node.remove(&entry.child_key).expect("evict failed");
349 self.change_size(0 - (entry.size as i64));
350 evicted += entry.size;
351 }
352 }
353 _ => break,
354 }
355 }
356 debug!("evict done");
357 }
358}
359
360#[async_trait]
361impl Actor for SledStorage {
362 async fn handle(&mut self, message: Message, ctx: &ActorContext) {
363 debug!("SledStorage incoming message {:?}", message);
364 match message {
365 Message::Get(get) => self.handle_get(get, ctx),
366 Message::Put(put) => self.handle_put(put),
367 _ => {}
368 }
369 }
370
371 async fn pre_start(&mut self, ctx: &ActorContext) {
372 info!("SledStorage adapter starting");
373 if let Some(limit) = self.max_size {
374 let storage = self.clone();
375 ctx.child_task(async move {
376 loop {
377 sleep(Duration::from_millis(2000)).await;
378 match storage.get_size() {
379 Ok(size) => {
380 if size >= limit {
381 let excess = size - limit;
382 info!(
383 "sled db too big ({} bytes), evicting lowest priority data",
384 size
385 );
386 storage.evict(excess); sleep(Duration::from_millis(1000)).await;
388 let new_size = storage.get_size().unwrap();
389 info!("size after eviction {} (was {} bytes)", new_size, size);
390 }
391 }
392 _ => {}
393 }
394 }
395 });
396 }
397 }
398
399 async fn stopping(&mut self, _context: &ActorContext) {
400 info!("SledStorage adapter stopping");
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use crate::actor::{ActorContext, Addr};
407 use crate::adapters::sled_storage::SledStorage;
408 use crate::message::{Get, Message, Put};
409 use crate::types::{Children, NodeData, Value};
410 use crate::Config;
411 use tokio::sync::mpsc::unbounded_channel;
412 use tokio::time::{sleep, Duration};
413
414 #[tokio::test]
415 async fn should_not_reply_with_put_when_checksum_same_as_in_get() {
416 let path = std::path::Path::new("./sled_checksum_test_db");
417 let (sender, mut receiver) = unbounded_channel::<Message>();
418 let return_addr = Addr::new(sender);
419 let sled_storage = SledStorage::new_with_config(
420 Config::default(),
421 sled::Config::default().path(path),
422 None,
423 );
424 let mut ctx = ActorContext::new("peer_id".to_string());
425 let sled_addr = ctx.start_actor(Box::new(sled_storage));
426
427 let mut children = Children::default();
428 children.insert(
429 "about".to_string(),
430 NodeData {
431 value: Value::Text("Jesus Built My Hot Rod".to_string()),
432 updated_at: 0 as f64,
433 },
434 );
435 let mut put = Put::new_from_kv("profile".to_string(), children, return_addr.clone());
436 put.to_string();
437 let checksum = put.checksum.clone().unwrap();
438 sled_addr.send(Message::Put(put)).ok();
439 sleep(Duration::from_millis(10)).await;
440 let mut get = Get::new(
441 "profile".to_string(),
442 Some("about".to_string()),
443 return_addr.clone(),
444 );
445
446 get.checksum = Some(checksum);
447 sled_addr.send(Message::Get(get.clone())).ok();
448 sleep(Duration::from_millis(10)).await;
449 assert!(receiver.try_recv().is_err());
450
451 get.checksum = None;
452 sled_addr.send(Message::Get(get)).ok();
453 sleep(Duration::from_millis(10)).await;
454 assert!(receiver.try_recv().is_ok());
455
456 std::fs::remove_dir_all(path).ok();
457
458 ctx.stop();
459 }
460}