diff --git a/prdoc/pr_11332.prdoc b/prdoc/pr_11332.prdoc new file mode 100644 index 000000000000..b4021a873f82 --- /dev/null +++ b/prdoc/pr_11332.prdoc @@ -0,0 +1,22 @@ +title: 'client/db: Close missing body gaps for non archive nodes' +doc: +- audience: Node Dev + description: |- + This PR closes missing body gaps in the database for non-archive nodes. + + + Effectively, a missing body gap cannot be closed on the DB side if the node is non-archive. Since execution is already skipped, the node will close the memory gap in the sync engine; however, the gap remains open in the db. + + This leads to wasting resources at every startup: + - client info contains a gap that cannot be filled (since we don't have the state around for execution) + - blocks are fetched from the connected peers + - gap is filled by ignoring blocks in the sync engine + + Further, for collators on origin master this causes an infinite loop of sync engine restarts that get punished via banning and disconnecting. For more details and root cause check: + - https://github.com/paritytech/polkadot-sdk/pull/11330 + + Part of: + - https://github.com/paritytech/polkadot-sdk/issues/11299 +crates: +- name: sc-client-db + bump: patch diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index d86b722963b5..a6489f367dba 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -1289,6 +1289,30 @@ impl Backend { }); } + // Non archive nodes cannot fill the missing block gap with bodies. + // If the gap is present, it means that every restart will try to fill the gap: + // - a block request is made for each and every block in the gap + // - the request is fulfilled putting pressure on the network and other nodes + // - upon receiving the block, the block cannot be executed since the state + // of the parent block might have been discarded + // - then the sync engine closes the gap in memory, but never in DB. + // + // This leads to inefficient syncing and high CPU usage on every restart. To mitigate this, + // remove the gap from the DB if we detect it and the current node is not an archive. + match (backend.is_archive, info.block_gap) { + (false, Some(gap)) if matches!(gap.gap_type, BlockGapType::MissingBody) => { + warn!( + "Detected a missing body gap for non-archive nodes. Removing the gap={:?}", + gap + ); + + db_init_transaction.remove(columns::META, meta_keys::BLOCK_GAP); + db_init_transaction.remove(columns::META, meta_keys::BLOCK_GAP_VERSION); + backend.blockchain.update_block_gap(None); + }, + _ => {}, + } + db.commit(db_init_transaction)?; Ok(backend)