Browse Source

Fixed bug with PERSIST (#28)

PERSIST operation did not remove the key from the purge process.
Microredis does have a dedicated structure to watch keys with
expirations in it.

Previous this commit if a key had any expiration and then this
expiration was removed with the PERSIST command, the expiration would be
removed from the key-itself, but the purge process (which is a
background process) wouldn't know about this change removing the key in
due time.

This commit notifies of PERSIST changes to the purge process.
César D. Rodas 3 years ago
parent
commit
0163e48063
2 changed files with 21 additions and 0 deletions
  1. 4 0
      src/db/expiration.rs
  2. 17 0
      src/db/mod.rs

+ 4 - 0
src/db/expiration.rs

@@ -46,6 +46,10 @@ impl ExpirationDb {
         self.next_id += 1;
     }
 
+    pub fn has(&self, key: &Bytes) -> bool {
+        self.keys.get(key).is_some()
+    }
+
     pub fn remove(&mut self, key: &Bytes) -> bool {
         if let Some(prev) = self.keys.remove(key) {
             // Another key with expiration is already known, it has

+ 17 - 0
src/db/mod.rs

@@ -220,6 +220,7 @@ impl Db {
             .filter(|x| x.is_valid())
             .map_or(0.into(), |x| {
                 if x.has_ttl() {
+                    self.expirations.lock().remove(key);
                     x.persist();
                     1.into()
                 } else {
@@ -387,6 +388,12 @@ impl Db {
             .map(|x| x.get_ttl())
     }
 
+    /// Check whether a given key is in the list of keys to be purged or not.
+    /// This function is mainly used for unit testing
+    pub fn is_key_in_expiration_list(&self, key: &Bytes) -> bool {
+        self.expirations.lock().has(key)
+    }
+
     /// Remove expired entries from the database.
     ///
     /// This function should be called from a background thread every few seconds. Calling it more
@@ -518,6 +525,16 @@ mod test {
     }
 
     #[test]
+    fn persist_bug() {
+        let db = Db::new(100);
+        db.set(&bytes!(b"one"), Value::Ok, Some(Duration::from_secs(1)));
+        assert_eq!(Value::Ok, db.get(&bytes!(b"one")));
+        assert!(db.is_key_in_expiration_list(&bytes!(b"one")));
+        db.persist(&bytes!(b"one"));
+        assert!(!db.is_key_in_expiration_list(&bytes!(b"one")));
+    }
+
+    #[test]
     fn purge_keys() {
         let db = Db::new(100);
         db.set(&bytes!(b"one"), Value::Ok, Some(Duration::from_secs(0)));