fix(install): mempool no longer blocked by a resyncing ElectrumX + slower podman probes tolerated

Two real install failures from .116 today: (1) the hard "ElectrumX must
be running" gate — but mempool-api reconnects to electrum on its own and
a resync can take days, so it's now a warning; (2) a 10s podman-ps probe
timeout tripped by ElectrumX compaction disk load — now 45s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-22 17:30:48 -04:00
parent b3796546aa
commit 581dbd6337
2 changed files with 40 additions and 29 deletions

View File

@ -206,19 +206,24 @@ pub(super) fn check_install_deps(package_id: &str, deps: &RunningDeps) -> Result
"BTCPay Server requires a running Bitcoin node (Bitcoin Knots). \
Please install and start Bitcoin Knots first."
)),
"mempool" | "mempool-web" if !deps.has_bitcoin || !deps.has_electrumx => {
let mut missing = vec![];
if !deps.has_bitcoin {
missing.push("Bitcoin Knots");
}
if !deps.has_electrumx {
missing.push("ElectrumX");
}
Err(anyhow::anyhow!(
"Mempool requires {} to be running. Please install and start {} first.",
missing.join(" and "),
missing.join(" and ")
))
"mempool" | "mempool-web" if !deps.has_bitcoin => Err(anyhow::anyhow!(
"Mempool requires Bitcoin Knots to be running. \
Please install and start Bitcoin Knots first."
)),
// ElectrumX is deliberately NOT a hard gate for mempool: mempool-api
// reconnects to electrum on its own (verified live — it retries
// ECONNREFUSED in a loop and comes good when electrum starts
// listening), and an ElectrumX mid-resync can be legitimately down
// for DAYS. The hard gate here blocked repeated mempool installs on
// .116 (2026-07-22) while electrumx was compacting. Block/fee views
// work from bitcoind immediately; address lookups light up when
// ElectrumX finishes.
"mempool" | "mempool-web" if !deps.has_electrumx => {
info!(
"Mempool installing while ElectrumX is not running — \
mempool-api reconnects automatically once ElectrumX is up"
);
Ok(())
}
"fedimint" if !deps.has_bitcoin => {
info!("Fedimint installing without local Bitcoin node — configure remote Bitcoin RPC in Fedimint guardian setup");
@ -298,9 +303,10 @@ fn missing_install_deps(package_id: &str, deps: &RunningDeps) -> Vec<MissingDep>
if !deps.has_bitcoin {
missing.push(BITCOIN);
}
if !deps.has_electrumx {
missing.push(ELECTRUM);
}
// ElectrumX intentionally not required — see check_install_deps:
// mempool-api reconnects when electrum comes up, and a resyncing
// ElectrumX can be down for days (the 3-minute bounded wait here
// would still fail the install for no benefit).
}
// fedimint deliberately absent: check_install_deps allows it without
// a local Bitcoin node (remote RPC configured in guardian setup).
@ -1111,7 +1117,11 @@ mod tests {
}
#[tokio::test]
async fn mempool_waits_on_both_bitcoin_and_electrumx() {
async fn mempool_waits_on_bitcoin_only() {
// ElectrumX is deliberately NOT gated (2026-07-22): a resyncing
// ElectrumX can be down for days and mempool-api reconnects on
// its own, so the wait only covers Bitcoin — even when electrumx
// is installed and stopped.
let calls = Arc::new(AtomicU32::new(0));
let (labels, sink) = label_sink();
let probe_calls = Arc::clone(&calls);
@ -1119,8 +1129,8 @@ mod tests {
"mempool",
move || {
let n = probe_calls.fetch_add(1, Ordering::SeqCst);
// Bitcoin comes up on probe 2, electrumx on probe 3.
async move { Ok(probe(n >= 1, n >= 2, &["bitcoin-knots", "electrumx"])) }
// Bitcoin comes up on probe 2; electrumx never does.
async move { Ok(probe(n >= 1, false, &["bitcoin-knots", "electrumx"])) }
},
sink,
36,
@ -1131,22 +1141,19 @@ mod tests {
let labels = labels.lock().unwrap();
assert_eq!(
labels.as_slice(),
&[
"Waiting for Bitcoin and ElectrumX to start…".to_string(),
"Waiting for ElectrumX to start…".to_string(),
]
&["Waiting for Bitcoin to start…".to_string()]
);
}
#[tokio::test]
async fn mempool_fails_fast_when_one_dep_is_not_installed() {
// Bitcoin is installed (waiting could help) but ElectrumX is not
// installed at all — waiting can never satisfy the gate, so fail
// fast with the canonical message.
async fn mempool_fails_fast_when_bitcoin_is_not_installed() {
// Bitcoin isn't installed at all — waiting can never satisfy the
// gate, so fail fast with the canonical message. (A missing
// ElectrumX no longer blocks anything.)
let (labels, sink) = label_sink();
let err = wait_for_install_deps(
"mempool",
|| async { Ok(probe(false, false, &["bitcoin-knots"])) },
|| async { Ok(probe(false, false, &["electrumx"])) },
sink,
36,
Duration::ZERO,

View File

@ -12,7 +12,11 @@ use tracing::info;
use super::install::{install_log, patch_indeedhub_nostr_provider};
const PODMAN_STACK_PROBE_TIMEOUT: Duration = Duration::from_secs(10);
// 45s, not 10s: under heavy disk load (e.g. an ElectrumX resync/compaction
// hammering the same SSD) a plain `podman ps` was observed taking >10s on
// .116 (2026-07-22), failing a mempool install at the probe step for no real
// reason. Podman being slow is not podman being dead.
const PODMAN_STACK_PROBE_TIMEOUT: Duration = Duration::from_secs(45);
const PODMAN_STACK_LOG_TIMEOUT: Duration = Duration::from_secs(15);
const PODMAN_STACK_PULL_TIMEOUT: Duration = Duration::from_secs(600);
const PODMAN_STACK_REMOVE_TIMEOUT: Duration = Duration::from_secs(90);