gui: installer: more robust poll for cookie file at bitcoind startup

We would formerly wait a definite number of seconds for the cookie file
to appear. But this approach presents a fundamental issue: how long is
enough before we can be reasonably sure bitcoind will never start?

For instance on mainnet the checks performed at startup could take more
than the 3 seconds we would previously wait for. If it does we would
have incorrectly errored and let bitcoind run in the background.

Instead, assume bitcoind would exit if it errors. Until it does so,
continue polling for the cookie file. This approach also presents
drawbacks (for instance what if bitcoind is performing a very long
operation before creating the cookie file?), but the former approach
wouldn't be an acceptable solution in this case either. And the new one
is preferable as its failure scenario seems much less probable.
This commit is contained in:
Antoine Poinsot 2023-08-31 12:50:50 +02:00
parent 927b031301
commit 76bb74371f
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
2 changed files with 29 additions and 28 deletions

View File

@ -4,6 +4,8 @@ use liana::{
};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::thread;
use std::time;
use tokio::sync::Mutex;
use tracing::{info, warn};
@ -187,24 +189,39 @@ impl Bitcoind {
.spawn()
.map_err(|e| StartInternalBitcoindError::CommandError(e.to_string()))?;
if !crate::utils::poll_for_file(&config.cookie_path, 200, 15) {
match process.wait_with_output() {
Err(e) => {
tracing::error!("Error while waiting for bitcoind to finish: {}", e)
}
Ok(o) => {
tracing::error!("Exit status: {}", o.status);
tracing::error!("stderr: {}", String::from_utf8_lossy(&o.stderr));
// We've started bitcoind in the background, however it may fail to start for whatever
// reason. And we need its JSONRPC interface to be available to continue. Thus wait for it
// to have created the cookie file, regularly checking it did not fail to start.
loop {
match process.try_wait() {
Ok(None) => {}
Err(e) => log::error!("Error while trying to wait for bitcoind: {}", e),
Ok(Some(status)) => {
log::error!("Bitcoind exited with status '{}'", status);
match process.wait_with_output() {
Err(e) => {
tracing::error!("Error while waiting for bitcoind to finish: {}", e)
}
Ok(o) => {
tracing::error!("stderr: {}", String::from_utf8_lossy(&o.stderr));
}
}
return Err(StartInternalBitcoindError::CookieFileNotFound(
config.cookie_path.to_string_lossy().into_owned(),
));
}
}
return Err(StartInternalBitcoindError::CookieFileNotFound(
config.cookie_path.to_string_lossy().into_owned(),
));
if config.cookie_path.exists() {
log::info!("Bitcoind seems to have successfully started.");
break;
}
log::info!("Waiting for bitcoind to start.");
thread::sleep(time::Duration::from_millis(500));
}
config.cookie_path = config.cookie_path.canonicalize().map_err(|e| {
StartInternalBitcoindError::CouldNotCanonicalizeCookiePath(e.to_string())
})?;
liana::BitcoinD::new(&config, "internal_bitcoind_start".to_string())
.map_err(|e| StartInternalBitcoindError::BitcoinDError(e.to_string()))?;

View File

@ -1,21 +1,5 @@
use std::path::Path;
#[cfg(test)]
pub mod sandbox;
#[cfg(test)]
pub mod mock;
/// Polls for a file's existence at given interval up to a maximum number of polls.
/// Returns `true` once file exists and otherwise `false`.
pub fn poll_for_file(path: &Path, interval_millis: u64, max_polls: u16) -> bool {
for i in 0..max_polls {
if path.exists() {
return true;
}
if i < max_polls.saturating_sub(1) {
std::thread::sleep(std::time::Duration::from_millis(interval_millis));
}
}
false
}