changeset 323:c8288b1ca724

snapshot: Implement snapshot auto-cleanup.
author Lewin Bormann <lbo@spheniscida.de>
date Sun, 01 Oct 2017 14:17:36 +0000
parents 06fe2447561b
children 4637c87d8b47
files src/snapshot.rs
diffstat 1 files changed, 80 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/src/snapshot.rs	Sun Oct 01 13:36:53 2017 +0000
+++ b/src/snapshot.rs	Sun Oct 01 14:17:36 2017 +0000
@@ -1,70 +1,92 @@
 use std::collections::HashMap;
-use types::SequenceNumber;
+use types::{share, MAX_SEQUENCE_NUMBER, SequenceNumber, Shared};
 
 /// Opaque snapshot handle; Represents index to SnapshotList.map
-pub type Snapshot = u64;
+type SnapshotHandle = u64;
+
+pub struct Snapshot {
+    id: SnapshotHandle,
+    sl: Shared<InnerSnapshotList>,
+}
+
+impl Drop for Snapshot {
+    fn drop(&mut self) {
+        self.sl.borrow_mut().delete(self.id);
+    }
+}
 
 /// A list of all snapshots is kept in the DB.
+struct InnerSnapshotList {
+    map: HashMap<SnapshotHandle, SequenceNumber>,
+    newest: SnapshotHandle,
+    oldest: SnapshotHandle,
+}
+
 pub struct SnapshotList {
-    map: HashMap<Snapshot, SequenceNumber>,
-    newest: Snapshot,
-    oldest: Snapshot,
+    inner: Shared<InnerSnapshotList>,
 }
 
 impl SnapshotList {
     pub fn new() -> SnapshotList {
         SnapshotList {
-            map: HashMap::new(),
-            newest: 0,
-            oldest: 0,
+            inner: share(InnerSnapshotList {
+                map: HashMap::new(),
+                newest: 0,
+                oldest: 0,
+            }),
         }
     }
 
     pub fn new_snapshot(&mut self, seq: SequenceNumber) -> Snapshot {
-        self.newest += 1;
-        self.map.insert(self.newest, seq);
+        let inner = self.inner.clone();
+        let mut sl = self.inner.borrow_mut();
 
-        if self.oldest == 0 {
-            self.oldest = self.newest;
+        sl.newest += 1;
+        let newest = sl.newest;
+        sl.map.insert(newest, seq);
+
+        if sl.oldest == 0 {
+            sl.oldest = sl.newest;
         }
 
-        self.newest
+        Snapshot {
+            id: sl.newest,
+            sl: inner,
+        }
     }
 
     pub fn sequence_at(&self, ss: &Snapshot) -> Option<SequenceNumber> {
-        self.map.get(ss).map(|sn| *sn)
-    }
-
-    pub fn oldest(&self) -> SequenceNumber {
-        self.map.get(&self.oldest).unwrap().clone()
-    }
-
-    pub fn newest(&self) -> SequenceNumber {
-        self.map.get(&self.newest).unwrap().clone()
+        let sl = self.inner.borrow_mut();
+        sl.map.get(&ss.id).map(|sn| *sn)
     }
 
-    pub fn delete(&mut self, ss: Snapshot) {
-        self.map.remove(&ss);
-        if self.oldest == ss {
-            self.oldest = self.newest;
-            for (seq, _) in self.map.iter() {
-                if *seq < self.oldest {
-                    self.oldest = *seq;
-                }
-            }
-        }
-        if self.newest == ss {
-            self.newest = self.oldest;
-            for (seq, _) in self.map.iter() {
-                if *seq > self.newest {
-                    self.newest = *seq;
-                }
-            }
+    /// oldest returns the lowest sequence number of all snapshots. It returns 0 if no snapshots
+    /// are present.
+    pub fn oldest(&self) -> SequenceNumber {
+        let oldest =
+            self.inner.borrow().map.iter().fold(MAX_SEQUENCE_NUMBER,
+                                                |s, (seq, _)| if *seq < s { *seq } else { s });
+        if oldest == MAX_SEQUENCE_NUMBER {
+            0
+        } else {
+            oldest
         }
     }
 
+    /// newest returns the newest sequence number of all snapshots. If no snapshots are present, it
+    /// returns 0.
+    pub fn newest(&self) -> SequenceNumber {
+        self.inner.borrow().map.iter().fold(0, |s, (seq, _)| if *seq > s { *seq } else { s })
+    }
+
     pub fn empty(&self) -> bool {
-        self.oldest == 0
+        self.inner.borrow().oldest == 0
+    }
+}
+
+impl InnerSnapshotList {
+    fn delete(&mut self, id: SnapshotHandle) {
+        self.map.remove(&id);
     }
 }
 
@@ -72,29 +94,32 @@
 mod tests {
     use super::*;
 
+    #[allow(unused_variables)]
     #[test]
     fn test_snapshot_list() {
         let mut l = SnapshotList::new();
 
-        assert!(l.empty());
+        {
+            assert!(l.empty());
+            let a = l.new_snapshot(1);
 
-        let oldest = l.new_snapshot(1);
-        l.new_snapshot(2);
-        let newest = l.new_snapshot(0);
+            {
+                let b = l.new_snapshot(2);
 
-        assert!(!l.empty());
-
-        assert_eq!(l.oldest(), 1);
-        assert_eq!(l.newest(), 0);
+                {
+                    let c = l.new_snapshot(3);
 
-        l.delete(newest);
-
-        assert_eq!(l.newest(), 2);
-        assert_eq!(l.oldest(), 1);
+                    assert!(!l.empty());
+                    assert_eq!(l.oldest(), 1);
+                    assert_eq!(l.newest(), 3);
+                }
 
-        l.delete(oldest);
+                assert_eq!(l.newest(), 2);
+                assert_eq!(l.oldest(), 1);
+            }
 
-        assert_eq!(l.oldest(), 2);
+            assert_eq!(l.oldest(), 1);
+        }
+        assert_eq!(l.oldest(), 0);
     }
-
 }