]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/backup/chunk_store.rs: fix GC
authorDietmar Maurer <dietmar@proxmox.com>
Sun, 31 Mar 2019 15:21:36 +0000 (17:21 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Sun, 31 Mar 2019 15:21:36 +0000 (17:21 +0200)
Added option to get oldest_writer timestamp from ProcessLocker.

src/backup/chunk_store.rs
src/backup/datastore.rs
src/tools/process_locker.rs

index 7dfff0af6c60c718c87fa543f691d5b3668c5f57..4be684e83ba43f1a96b00fdec8e8603249039bfd 100644 (file)
@@ -247,11 +247,29 @@ impl ChunkStore {
         }))
     }
 
-    pub fn sweep_unused_chunks(&self, status: &mut GarbageCollectionStatus) -> Result<(), Error> {
+    pub fn oldest_writer(&self) -> Option<i64> {
+        tools::ProcessLocker::oldest_shared_lock(self.locker.clone())
+    }
+
+    pub fn sweep_unused_chunks(
+        &self,
+        oldest_writer: Option<i64>,
+        status: &mut GarbageCollectionStatus
+    ) -> Result<(), Error> {
         use nix::sys::stat::fstatat;
 
         let now = unsafe { libc::time(std::ptr::null_mut()) };
 
+        let mut min_atime = now - 3600*24; // at least 24h (see mount option relatime)
+
+        if let Some(stamp) = oldest_writer {
+            if stamp < min_atime {
+                min_atime = stamp;
+            }
+        }
+
+        min_atime -= 300; // add 5 mins gap for safety
+
         for entry in self.get_chunk_iterator(true)? {
             let (dirfd, entry) = match entry {
                 Ok(entry) => (entry.parent_fd(), entry),
@@ -273,7 +291,7 @@ impl ChunkStore {
             if let Ok(stat) = fstatat(dirfd, filename, nix::fcntl::AtFlags::AT_SYMLINK_NOFOLLOW) {
                 let age = now - stat.st_atime;
                 //println!("FOUND {}  {:?}", age/(3600*24), filename);
-                if age/(3600*24) >= 2 {
+                if stat.st_atime < min_atime {
                     println!("UNLINK {}  {:?}", age/(3600*24), filename);
                     let res = unsafe { libc::unlinkat(dirfd, filename.as_ptr(), 0) };
                     if res != 0 {
index ca0787988e39ea5a2a6364484888b1ef5dae170d..cfd7c36af851b6aae3089db647b0a65ae0c02f59 100644 (file)
@@ -229,6 +229,8 @@ impl DataStore {
 
             let _exclusive_lock =  self.chunk_store.try_exclusive_lock()?;
 
+            let oldest_writer = self.chunk_store.oldest_writer();
+
             let mut gc_status = GarbageCollectionStatus::default();
             gc_status.used_bytes = 0;
 
@@ -237,7 +239,7 @@ impl DataStore {
             self.mark_used_chunks(&mut gc_status)?;
 
             println!("Start GC phase2 (sweep unused chunks)");
-            self.chunk_store.sweep_unused_chunks(&mut gc_status)?;
+            self.chunk_store.sweep_unused_chunks(oldest_writer, &mut gc_status)?;
 
             println!("Used bytes: {}", gc_status.used_bytes);
             println!("Used chunks: {}", gc_status.used_chunks);
index 45c3ab227a5211b06f7451c8f2692589fbfdf097..e60992d623bbcae77e94d73504d883c2df286bb8 100644 (file)
@@ -2,11 +2,16 @@
 //!
 //! This implemenation uses fcntl record locks with non-blocking
 //! F_SETLK command (never blocks).
+//!
+//! We maintain a map of shared locks with time stamps, so you can get
+//! the timestamp for the oldest open lock with
+//! `oldest_shared_lock()`.
 
 use failure::*;
 
 use std::sync::{Arc, Mutex};
 use std::os::unix::io::AsRawFd;
+use std::collections::HashMap;
 
 // fixme: use F_OFD_ locks when implemented with nix::fcntl
 
@@ -17,12 +22,15 @@ pub struct ProcessLocker {
     file: std::fs::File,
     exclusive: bool,
     writers: usize,
+    next_guard_id: u64,
+    shared_guard_list: HashMap<u64, i64>, // guard_id => timestamp
 }
 
 /// Lock guard for shared locks
 ///
 /// Release the lock when it goes out of scope.
 pub struct ProcessLockSharedGuard {
+    guard_id: u64,
     locker: Arc<Mutex<ProcessLocker>>,
 }
 
@@ -32,6 +40,8 @@ impl  Drop for ProcessLockSharedGuard {
 
         if data.writers == 0 { panic!("unexpected ProcessLocker state"); }
 
+        data.shared_guard_list.remove(&self.guard_id);
+
         if data.writers == 1 && !data.exclusive {
 
             let op = libc::flock {
@@ -97,6 +107,8 @@ impl ProcessLocker {
             file: file,
             exclusive: false,
             writers: 0,
+            next_guard_id: 0,
+            shared_guard_list: HashMap::new(),
         })))
     }
 
@@ -130,7 +142,30 @@ impl ProcessLocker {
 
         data.writers += 1;
 
-        Ok(ProcessLockSharedGuard { locker: locker.clone()  })
+        let guard = ProcessLockSharedGuard { locker: locker.clone(), guard_id: data.next_guard_id };
+        data.next_guard_id += 1;
+
+        let now = unsafe { libc::time(std::ptr::null_mut()) };
+
+        data.shared_guard_list.insert(guard.guard_id, now);
+
+        Ok(guard)
+    }
+
+    /// Get oldest shared lock timestamp
+    pub fn oldest_shared_lock(locker: Arc<Mutex<Self>>) -> Option<i64> {
+        let mut result = None;
+
+        let data = locker.lock().unwrap();
+
+        for (_k, v) in &data.shared_guard_list {
+            result = match result {
+                None => Some(*v),
+                Some(x) => if x < *v { Some(x) } else { Some(*v) },
+            };
+        }
+
+        result
     }
 
     /// Try to aquire a exclusive lock