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(),
|
limit: FetchLimit::default(),
|
||||||
info: git::UserInfo { alias, key: nid },
|
info: git::UserInfo { alias, key: nid },
|
||||||
local: nid,
|
local: nid,
|
||||||
|
expiry: worker::garbage::Expiry::default(),
|
||||||
};
|
};
|
||||||
let pool = worker::Pool::with(
|
let pool = worker::Pool::with(
|
||||||
worker_recv,
|
worker_recv,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ mod channels;
|
||||||
mod upload_pack;
|
mod upload_pack;
|
||||||
|
|
||||||
pub mod fetch;
|
pub mod fetch;
|
||||||
|
pub mod garbage;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{io, time};
|
use std::{io, time};
|
||||||
|
|
@ -162,6 +163,9 @@ pub struct FetchConfig {
|
||||||
pub info: git::UserInfo,
|
pub info: git::UserInfo,
|
||||||
/// Public key of the local peer.
|
/// Public key of the local peer.
|
||||||
pub local: crypto::PublicKey,
|
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.
|
/// A worker that replicates git objects.
|
||||||
|
|
@ -279,6 +283,7 @@ impl Worker {
|
||||||
limit,
|
limit,
|
||||||
info,
|
info,
|
||||||
local,
|
local,
|
||||||
|
expiry,
|
||||||
} = &self.fetch_config;
|
} = &self.fetch_config;
|
||||||
let tracking =
|
let tracking =
|
||||||
tracking::Config::new(*policy, *scope, tracking::Store::reader(tracking_db)?);
|
tracking::Config::new(*policy, *scope, tracking::Store::reader(tracking_db)?);
|
||||||
|
|
@ -296,8 +301,15 @@ impl Worker {
|
||||||
blocked,
|
blocked,
|
||||||
channels,
|
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