fix: pull timeout covers entire operation, swap registry priority

Timeout now wraps stderr reader + wait (was only wrapping wait, so
hung pulls were never killed). 23.182.128.160:3000 is now primary
registry since git.tx1138.com is unreachable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-04-12 10:18:24 -04:00
parent bcf7ac1839
commit 6ff347a503
3 changed files with 37 additions and 35 deletions

View File

@ -597,42 +597,44 @@ impl RpcHandler {
.spawn() .spawn()
.context("Failed to start image pull")?; .context("Failed to start image pull")?;
if let Some(stderr) = child.stderr.take() { // Wrap the entire pull (stderr progress + wait) in a 60s timeout.
let reader = BufReader::new(stderr); // If the registry is unreachable, the pull hangs on DNS/TCP and the
let mut lines = reader.lines(); // stderr reader never returns — so the timeout must cover everything.
let pkg_id = package_id.to_string(); let pull_result = tokio::time::timeout(
let state_mgr = self.state_manager.clone();
while let Ok(Some(line)) = lines.next_line().await {
if let Some((downloaded, total)) = parse_pull_progress(&line) {
Self::update_install_progress(&state_mgr, &pkg_id, downloaded, total)
.await;
}
}
}
// Timeout primary pull after 60s — if registry is down, fail fast to fallback
let timed_out;
let status = match tokio::time::timeout(
std::time::Duration::from_secs(60), std::time::Duration::from_secs(60),
child.wait(), async {
if let Some(stderr) = child.stderr.take() {
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
let pkg_id = package_id.to_string();
let state_mgr = self.state_manager.clone();
while let Ok(Some(line)) = lines.next_line().await {
if let Some((downloaded, total)) = parse_pull_progress(&line) {
Self::update_install_progress(&state_mgr, &pkg_id, downloaded, total)
.await;
}
}
}
child.wait().await
},
) )
.await .await;
{
Ok(result) => { let primary_failed = match pull_result {
timed_out = false; Ok(Ok(status)) => !status.success(),
result.context("Failed to wait for image pull")? Ok(Err(e)) => {
tracing::warn!("Image pull process error: {}", e);
true
} }
Err(_) => { Err(_) => {
// Timeout: kill the stuck process
tracing::warn!("Image pull timed out after 60s: {}", docker_image); tracing::warn!("Image pull timed out after 60s: {}", docker_image);
let _ = child.kill().await; let _ = child.kill().await;
timed_out = true; let _ = child.wait().await; // reap zombie
// Wait for process to actually exit after kill true
child.wait().await.context("Failed to wait after kill")?
} }
}; };
if timed_out || !status.success() { if primary_failed {
// Try all configured fallback registries dynamically // Try all configured fallback registries dynamically
match crate::container::registry::pull_from_registries( match crate::container::registry::pull_from_registries(
&self.config.data_dir, &self.config.data_dir,

View File

@ -44,16 +44,16 @@ impl Default for RegistryConfig {
Self { Self {
registries: vec![ registries: vec![
Registry { Registry {
url: "git.tx1138.com/lfg2025".to_string(), url: "23.182.128.160:3000/lfg2025".to_string(),
name: "Archipelago Primary".to_string(), name: "Archipelago Primary".to_string(),
tls_verify: true, tls_verify: false,
enabled: true, enabled: true,
priority: 0, priority: 0,
}, },
Registry { Registry {
url: "23.182.128.160:3000/lfg2025".to_string(), url: "git.tx1138.com/lfg2025".to_string(),
name: "Archipelago Fallback".to_string(), name: "Archipelago Legacy".to_string(),
tls_verify: false, tls_verify: true,
enabled: true, enabled: true,
priority: 10, priority: 10,
}, },

View File

@ -2132,8 +2132,8 @@ mkdir -p /mnt/target/var/lib/archipelago/config
cat > /mnt/target/var/lib/archipelago/config/registries.json <<'DYNREG' cat > /mnt/target/var/lib/archipelago/config/registries.json <<'DYNREG'
{ {
"registries": [ "registries": [
{"url": "git.tx1138.com/lfg2025", "name": "Archipelago Primary", "tls_verify": true, "enabled": true, "priority": 0}, {"url": "23.182.128.160:3000/lfg2025", "name": "Archipelago Primary", "tls_verify": false, "enabled": true, "priority": 0},
{"url": "23.182.128.160:3000/lfg2025", "name": "Archipelago Fallback", "tls_verify": false, "enabled": true, "priority": 10} {"url": "git.tx1138.com/lfg2025", "name": "Archipelago Legacy", "tls_verify": true, "enabled": true, "priority": 10}
] ]
} }
DYNREG DYNREG