node: run git gc on fetch
It is desirable to keep the Git repositories in Radicle efficient by
ensuring objects are packed and unreachable objects are removed.
Add a process call to `git gc` to achieve this. Two flags are used for
garbage collection: `--auto` and `--prune`. Their details are outlined below:
--auto
With this option, git gc checks whether any housekeeping is
required; if not, it exits without performing any work.
--prune=<date>
Prune loose objects older than date (default is 2 weeks
ago, overridable by the config variable
gc.pruneExpire). --prune=now prunes loose objects
regardless of their age and increases the risk of
corruption if another process is writing to the repository
concurrently; see "NOTES" below. --prune is on by default.
For `--prune` the default used is `1.hours.ago`. This was chosen over
`now` since that may be too aggressive after just fetching new data.
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
dd16356e4e
commit
cf08a3f731
|
|
@ -256,6 +256,7 @@ impl Runtime {
|
|||
limit: FetchLimit::default(),
|
||||
info: git::UserInfo { alias, key: nid },
|
||||
local: nid,
|
||||
expiry: worker::garbage::Expiry::default(),
|
||||
};
|
||||
let pool = worker::Pool::with(
|
||||
worker_recv,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ mod channels;
|
|||
mod upload_pack;
|
||||
|
||||
pub mod fetch;
|
||||
pub mod garbage;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{io, time};
|
||||
|
|
@ -162,6 +163,9 @@ pub struct FetchConfig {
|
|||
pub info: git::UserInfo,
|
||||
/// Public key of the local peer.
|
||||
pub local: crypto::PublicKey,
|
||||
/// Configuration for `git gc` garbage collection. Defaults to `1
|
||||
/// hour ago`.
|
||||
pub expiry: garbage::Expiry,
|
||||
}
|
||||
|
||||
/// A worker that replicates git objects.
|
||||
|
|
@ -279,6 +283,7 @@ impl Worker {
|
|||
limit,
|
||||
info,
|
||||
local,
|
||||
expiry,
|
||||
} = &self.fetch_config;
|
||||
let tracking =
|
||||
tracking::Config::new(*policy, *scope, tracking::Store::reader(tracking_db)?);
|
||||
|
|
@ -296,8 +301,15 @@ impl Worker {
|
|||
blocked,
|
||||
channels,
|
||||
)?;
|
||||
let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;
|
||||
|
||||
Ok(handle.fetch(rid, &self.storage, *limit, remote, refs_at)?)
|
||||
if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
|
||||
// N.b. ensure that `git gc` works in debug mode.
|
||||
debug_assert!(false, "`git gc` failed: {e}");
|
||||
|
||||
log::warn!(target: "worker", "Failed to run `git gc`: {e}");
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
use std::process::{Command, ExitStatus, Stdio};
|
||||
use std::{fmt, io};
|
||||
|
||||
use radicle::prelude::Id;
|
||||
use radicle::storage::ReadStorage;
|
||||
|
||||
/// Default expiry time for objects.
|
||||
pub const EXPIRY_DEFAULT: Expiry = Expiry::Hours(1);
|
||||
|
||||
/// Expiry of objects for garbage collector.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Expiry {
|
||||
Now,
|
||||
Seconds(usize),
|
||||
Hours(usize),
|
||||
Days(usize),
|
||||
Weeks(usize),
|
||||
}
|
||||
|
||||
impl Default for Expiry {
|
||||
fn default() -> Self {
|
||||
EXPIRY_DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Expiry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Now => f.write_str("now"),
|
||||
Self::Seconds(s) => write!(f, "{s}.seconds.ago"),
|
||||
Self::Hours(s) => write!(f, "{s}.hours.ago"),
|
||||
Self::Days(s) => write!(f, "{s}.days.ago"),
|
||||
Self::Weeks(s) => write!(f, "{s}.weeks.ago"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Git garbage collector.
|
||||
pub fn collect(storage: &impl ReadStorage, rid: Id, expiry: Expiry) -> io::Result<ExitStatus> {
|
||||
let git_dir = storage.path_of(&rid);
|
||||
let mut gc = Command::new("git");
|
||||
gc.current_dir(git_dir)
|
||||
.env_clear()
|
||||
.envs(std::env::vars().filter(|(key, _)| key == "PATH" || key.starts_with("GIT_TRACE")))
|
||||
.args(["gc", &format!("--prune={expiry}"), "--auto"])
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.stderr(Stdio::inherit());
|
||||
let mut child = gc.spawn()?;
|
||||
let status = child.wait()?;
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
Loading…
Reference in New Issue