手动执行全量同步
This commit is contained in:
@@ -2,6 +2,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
use sha2::Digest;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::fetch::rsync::{RsyncFetchError, RsyncFetchResult, RsyncFetcher};
|
||||
@@ -11,6 +12,14 @@ pub struct SystemRsyncConfig {
|
||||
pub rsync_bin: PathBuf,
|
||||
pub timeout: Duration,
|
||||
pub extra_args: Vec<String>,
|
||||
/// Optional root directory for persistent rsync mirrors.
|
||||
///
|
||||
/// When set, callers may choose to sync into stable subdirectories under this
|
||||
/// root (instead of a temporary directory) to benefit from rsync's incremental
|
||||
/// behavior across runs.
|
||||
///
|
||||
/// Note: actual mirror behavior is implemented separately from config wiring.
|
||||
pub mirror_root: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for SystemRsyncConfig {
|
||||
@@ -19,6 +28,7 @@ impl Default for SystemRsyncConfig {
|
||||
rsync_bin: PathBuf::from("rsync"),
|
||||
timeout: Duration::from_secs(60),
|
||||
extra_args: Vec::new(),
|
||||
mirror_root: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +47,29 @@ impl SystemRsyncFetcher {
|
||||
Self { config }
|
||||
}
|
||||
|
||||
fn mirror_dst_dir(&self, normalized_rsync_base_uri: &str) -> Result<Option<PathBuf>, String> {
|
||||
let Some(root) = self.config.mirror_root.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
std::fs::create_dir_all(root).map_err(|e| {
|
||||
format!(
|
||||
"create rsync mirror root failed: {}: {e}",
|
||||
root.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
let hash = hex::encode(sha2::Sha256::digest(normalized_rsync_base_uri.as_bytes()));
|
||||
let dir = root.join(hash);
|
||||
std::fs::create_dir_all(&dir).map_err(|e| {
|
||||
format!(
|
||||
"create rsync mirror directory failed: {}: {e}",
|
||||
dir.display()
|
||||
)
|
||||
})?;
|
||||
Ok(Some(dir))
|
||||
}
|
||||
|
||||
fn run_rsync(&self, src: &str, dst: &Path) -> Result<(), String> {
|
||||
// `--timeout` is I/O timeout in seconds (applies to network reads/writes).
|
||||
let timeout_secs = self.config.timeout.as_secs().max(1).to_string();
|
||||
@@ -70,8 +103,17 @@ impl SystemRsyncFetcher {
|
||||
impl RsyncFetcher for SystemRsyncFetcher {
|
||||
fn fetch_objects(&self, rsync_base_uri: &str) -> RsyncFetchResult<Vec<(String, Vec<u8>)>> {
|
||||
let base = normalize_rsync_base_uri(rsync_base_uri);
|
||||
let tmp = TempDir::new().map_err(|e| RsyncFetchError::Fetch(e.to_string()))?;
|
||||
if let Some(dst) = self
|
||||
.mirror_dst_dir(&base)
|
||||
.map_err(|e| RsyncFetchError::Fetch(e.to_string()))?
|
||||
{
|
||||
self.run_rsync(&base, &dst).map_err(RsyncFetchError::Fetch)?;
|
||||
let mut out = Vec::new();
|
||||
walk_dir_collect(&dst, &dst, &base, &mut out).map_err(RsyncFetchError::Fetch)?;
|
||||
return Ok(out);
|
||||
}
|
||||
|
||||
let tmp = TempDir::new().map_err(|e| RsyncFetchError::Fetch(e.to_string()))?;
|
||||
self.run_rsync(&base, tmp.path())
|
||||
.map_err(RsyncFetchError::Fetch)?;
|
||||
|
||||
@@ -187,6 +229,7 @@ mod tests {
|
||||
rsync_bin: PathBuf::from("/this/does/not/exist/rsync"),
|
||||
timeout: Duration::from_secs(1),
|
||||
extra_args: Vec::new(),
|
||||
mirror_root: None,
|
||||
});
|
||||
let e = f
|
||||
.run_rsync("rsync://example.net/repo/", dst.path())
|
||||
@@ -198,6 +241,7 @@ mod tests {
|
||||
rsync_bin: PathBuf::from("false"),
|
||||
timeout: Duration::from_secs(1),
|
||||
extra_args: Vec::new(),
|
||||
mirror_root: None,
|
||||
});
|
||||
let e = f
|
||||
.run_rsync("rsync://example.net/repo/", dst.path())
|
||||
|
||||
Reference in New Issue
Block a user