Skip to content

Commit 9c72ef4

Browse files
committed
Fix deadlock with many peers, few threads, and slow postgres writes
When we have many peers but relatively few threads, and postgres writes fall behind enough to block the async stream of gossip, we can end up deadlocking on the `PeerManager` `peers` thread. Specifically, because `PeerManager::process_events` holds a `peers` (read) lock while fetching messages (during which time we `block_in_place` trying to push gossip to the async stream), any other tasks which try to touch the `peers` lock in write mode (e.g. connecting to a peer or processing a disconnection) we'll hang until some postgres writes finish. If we get enough threads blocked, we end up blocking the tokio reactor and the postgres writes will stall, fully deadlocking. There's not a great solution to this, instead we just have to ensure we have enough threads which we enforce.
1 parent 010011a commit 9c72ef4

3 files changed

Lines changed: 25 additions & 6 deletions

File tree

src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ pub(crate) async fn upgrade_db<L: Deref + Clone + Send + Sync + 'static>(
362362
let _ = client.execute("ALTER TABLE channel_announcements SET ( autovacuum_vacuum_insert_scale_factor = 0.005 );", &[]).await;
363363
}
364364

365-
pub(crate) fn ln_peers() -> Vec<(PublicKey, SocketAddr)> {
365+
pub fn ln_peers() -> Vec<(PublicKey, SocketAddr)> {
366366
const WALLET_OF_SATOSHI: &str = "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226@170.75.163.209:9735";
367367
let list = env::var("LN_PEERS").unwrap_or(WALLET_OF_SATOSHI.to_string());
368368
let mut peers = Vec::new();

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ mod lookup;
3737
mod persistence;
3838
mod serialization;
3939
mod snapshot;
40-
mod config;
4140
mod hex_utils;
4241
mod verifier;
4342

43+
pub mod config;
4444
pub mod types;
4545

4646
#[cfg(test)]

src/main.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
use std::sync::Arc;
2+
23
use rapid_gossip_sync_server::RapidSyncProcessor;
4+
use rapid_gossip_sync_server::config;
35
use rapid_gossip_sync_server::types::RGSSLogger;
46

5-
#[tokio::main]
6-
async fn main() {
7-
let logger = Arc::new(RGSSLogger::new());
8-
RapidSyncProcessor::new(logger).start_sync().await;
7+
use tokio::runtime::Builder;
8+
9+
fn main() {
10+
Builder::new_multi_thread()
11+
.enable_all()
12+
// Ensure we have a nontrivial number of threads per peer. This works around a deadlock
13+
// where we have one thread doing `PeerManager::process_events` which does a
14+
// `block_in_place` with the `PeerManager` peers lock held (to push gossip into the gossip
15+
// persistence queue, if its full). Once this happens, if any peer operations happen for
16+
// new connections to peers or peer disconnections, they may wait on the `PeerManager`
17+
// peers mutex. If there aren't enough threads for at least one to be blocked on the
18+
// `PeerManger` peers mutex per (configured) peer, we may just end up hanging.
19+
// There isn't really a great way to fix it, so instead we just make sure we have plenty of
20+
// threads.
21+
.worker_threads(config::ln_peers().len() * 2 + 4)
22+
.build()
23+
.unwrap()
24+
.block_on(async {
25+
let logger = Arc::new(RGSSLogger::new());
26+
RapidSyncProcessor::new(logger).start_sync().await;
27+
});
928
}

0 commit comments

Comments
 (0)