手动执行全量同步

This commit is contained in:
2026-03-04 11:12:53 +08:00
parent 0f3d65254e
commit 6276d13814
54 changed files with 3879 additions and 670 deletions

View File

@@ -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())