Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 177e85f

Browse files
skunertmichalkucharczykbkchr
authored andcommitted
Notification-based block pinning (#13157)
* Worker * Reorganize and unpin onnotification drop * Pin in state-db, pass block number * Pin blocks in blockchain db * Switch to reference counted LRU * Disable pinning when we keep all blocks * Fix pinning hint for state-db * Remove pinning from backend layer * Improve readability * Add justifications to test * Fix justification behaviour * Remove debug prints * Convert channels to tracing_unbounded * Add comments to the test * Documentation and Cleanup * Move task start to client * Simplify cache * Improve test, remove unwanted log * Add tracing logs, remove expect for block number * Cleanup * Add conversion method for unpin handle to Finalitynotification * Revert unwanted changes * Improve naming * Make clippy happy * Fix docs Co-authored-by: Michal Kucharczyk <[email protected]> * Use `NumberFor` instead of u64 in API * Hand over weak reference to unpin worker task * Unwanted * &Hash -> Hash * Remove number from interface, rename `_unpin_handle`, LOG_TARGET * Move RwLock one layer up * Apply code style suggestions * Improve comments * Replace lru crate by schnellru * Only insert values for pinned items + better docs * Apply suggestions from code review Co-authored-by: Bastian Köcher <[email protected]> * Improve comments, log target and test Co-authored-by: Michal Kucharczyk <[email protected]> Co-authored-by: Bastian Köcher <[email protected]>
1 parent 6dd98d4 commit 177e85f

File tree

11 files changed

+954
-99
lines changed

11 files changed

+954
-99
lines changed

Cargo.lock

Lines changed: 41 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/api/src/backend.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,12 +436,24 @@ pub trait StorageProvider<Block: BlockT, B: Backend<Block>> {
436436
///
437437
/// Manages the data layer.
438438
///
439-
/// Note on state pruning: while an object from `state_at` is alive, the state
439+
/// # State Pruning
440+
///
441+
/// While an object from `state_at` is alive, the state
440442
/// should not be pruned. The backend should internally reference-count
441443
/// its state objects.
442444
///
443445
/// The same applies for live `BlockImportOperation`s: while an import operation building on a
444446
/// parent `P` is alive, the state for `P` should not be pruned.
447+
///
448+
/// # Block Pruning
449+
///
450+
/// Users can pin blocks in memory by calling `pin_block`. When
451+
/// a block would be pruned, its value is kept in an in-memory cache
452+
/// until it is unpinned via `unpin_block`.
453+
///
454+
/// While a block is pinned, its state is also preserved.
455+
///
456+
/// The backend should internally reference count the number of pin / unpin calls.
445457
pub trait Backend<Block: BlockT>: AuxStore + Send + Sync {
446458
/// Associated block insertion operation type.
447459
type BlockImportOperation: BlockImportOperation<Block, State = Self::State>;
@@ -502,6 +514,14 @@ pub trait Backend<Block: BlockT>: AuxStore + Send + Sync {
502514
/// Returns a handle to offchain storage.
503515
fn offchain_storage(&self) -> Option<Self::OffchainStorage>;
504516

517+
/// Pin the block to keep body, justification and state available after pruning.
518+
/// Number of pins are reference counted. Users need to make sure to perform
519+
/// one call to [`Self::unpin_block`] per call to [`Self::pin_block`].
520+
fn pin_block(&self, hash: Block::Hash) -> sp_blockchain::Result<()>;
521+
522+
/// Unpin the block to allow pruning.
523+
fn unpin_block(&self, hash: Block::Hash);
524+
505525
/// Returns true if state for given block is available.
506526
fn have_state_at(&self, hash: Block::Hash, _number: NumberFor<Block>) -> bool {
507527
self.state_at(hash).is_ok()

client/api/src/client.rs

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use std::{collections::HashSet, fmt, sync::Arc};
3030
use crate::{blockchain::Info, notifications::StorageEventStream, FinalizeSummary, ImportSummary};
3131

3232
use sc_transaction_pool_api::ChainEvent;
33-
use sc_utils::mpsc::TracingUnboundedReceiver;
33+
use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender};
3434
use sp_blockchain;
3535

3636
/// Type that implements `futures::Stream` of block import events.
@@ -264,6 +264,53 @@ impl fmt::Display for UsageInfo {
264264
}
265265
}
266266

267+
/// Sends a message to the pinning-worker once dropped to unpin a block in the backend.
268+
#[derive(Debug)]
269+
pub struct UnpinHandleInner<Block: BlockT> {
270+
/// Hash of the block pinned by this handle
271+
hash: Block::Hash,
272+
unpin_worker_sender: TracingUnboundedSender<Block::Hash>,
273+
}
274+
275+
impl<Block: BlockT> UnpinHandleInner<Block> {
276+
/// Create a new [`UnpinHandleInner`]
277+
pub fn new(
278+
hash: Block::Hash,
279+
unpin_worker_sender: TracingUnboundedSender<Block::Hash>,
280+
) -> Self {
281+
Self { hash, unpin_worker_sender }
282+
}
283+
}
284+
285+
impl<Block: BlockT> Drop for UnpinHandleInner<Block> {
286+
fn drop(&mut self) {
287+
if let Err(err) = self.unpin_worker_sender.unbounded_send(self.hash) {
288+
log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err);
289+
};
290+
}
291+
}
292+
293+
/// Keeps a specific block pinned while the handle is alive.
294+
/// Once the last handle instance for a given block is dropped, the
295+
/// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block).
296+
#[derive(Debug, Clone)]
297+
pub struct UnpinHandle<Block: BlockT>(Arc<UnpinHandleInner<Block>>);
298+
299+
impl<Block: BlockT> UnpinHandle<Block> {
300+
/// Create a new [`UnpinHandle`]
301+
pub fn new(
302+
hash: Block::Hash,
303+
unpin_worker_sender: TracingUnboundedSender<Block::Hash>,
304+
) -> UnpinHandle<Block> {
305+
UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender)))
306+
}
307+
308+
/// Hash of the block this handle is unpinning on drop
309+
pub fn hash(&self) -> Block::Hash {
310+
self.0.hash
311+
}
312+
}
313+
267314
/// Summary of an imported block
268315
#[derive(Clone, Debug)]
269316
pub struct BlockImportNotification<Block: BlockT> {
@@ -279,6 +326,36 @@ pub struct BlockImportNotification<Block: BlockT> {
279326
///
280327
/// If `None`, there was no re-org while importing.
281328
pub tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
329+
/// Handle to unpin the block this notification is for
330+
unpin_handle: UnpinHandle<Block>,
331+
}
332+
333+
impl<Block: BlockT> BlockImportNotification<Block> {
334+
/// Create new notification
335+
pub fn new(
336+
hash: Block::Hash,
337+
origin: BlockOrigin,
338+
header: Block::Header,
339+
is_new_best: bool,
340+
tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
341+
unpin_worker_sender: TracingUnboundedSender<Block::Hash>,
342+
) -> Self {
343+
Self {
344+
hash,
345+
origin,
346+
header,
347+
is_new_best,
348+
tree_route,
349+
unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
350+
}
351+
}
352+
353+
/// Consume this notification and extract the unpin handle.
354+
///
355+
/// Note: Only use this if you want to keep the block pinned in the backend.
356+
pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
357+
self.unpin_handle
358+
}
282359
}
283360

284361
/// Summary of a finalized block.
@@ -294,6 +371,8 @@ pub struct FinalityNotification<Block: BlockT> {
294371
pub tree_route: Arc<[Block::Hash]>,
295372
/// Stale branches heads.
296373
pub stale_heads: Arc<[Block::Hash]>,
374+
/// Handle to unpin the block this notification is for
375+
unpin_handle: UnpinHandle<Block>,
297376
}
298377

299378
impl<B: BlockT> TryFrom<BlockImportNotification<B>> for ChainEvent<B> {
@@ -314,26 +393,44 @@ impl<B: BlockT> From<FinalityNotification<B>> for ChainEvent<B> {
314393
}
315394
}
316395

317-
impl<B: BlockT> From<FinalizeSummary<B>> for FinalityNotification<B> {
318-
fn from(mut summary: FinalizeSummary<B>) -> Self {
396+
impl<Block: BlockT> FinalityNotification<Block> {
397+
/// Create finality notification from finality summary.
398+
pub fn from_summary(
399+
mut summary: FinalizeSummary<Block>,
400+
unpin_worker_sender: TracingUnboundedSender<Block::Hash>,
401+
) -> FinalityNotification<Block> {
319402
let hash = summary.finalized.pop().unwrap_or_default();
320403
FinalityNotification {
321404
hash,
322405
header: summary.header,
323406
tree_route: Arc::from(summary.finalized),
324407
stale_heads: Arc::from(summary.stale_heads),
408+
unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
325409
}
326410
}
411+
412+
/// Consume this notification and extract the unpin handle.
413+
///
414+
/// Note: Only use this if you want to keep the block pinned in the backend.
415+
pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
416+
self.unpin_handle
417+
}
327418
}
328419

329-
impl<B: BlockT> From<ImportSummary<B>> for BlockImportNotification<B> {
330-
fn from(summary: ImportSummary<B>) -> Self {
420+
impl<Block: BlockT> BlockImportNotification<Block> {
421+
/// Create finality notification from finality summary.
422+
pub fn from_summary(
423+
summary: ImportSummary<Block>,
424+
unpin_worker_sender: TracingUnboundedSender<Block::Hash>,
425+
) -> BlockImportNotification<Block> {
426+
let hash = summary.hash;
331427
BlockImportNotification {
332-
hash: summary.hash,
428+
hash,
333429
origin: summary.origin,
334430
header: summary.header,
335431
is_new_best: summary.is_new_best,
336432
tree_route: summary.tree_route.map(Arc::new),
433+
unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
337434
}
338435
}
339436
}

client/api/src/in_mem.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,12 @@ where
788788
fn requires_full_sync(&self) -> bool {
789789
false
790790
}
791+
792+
fn pin_block(&self, _: <Block as BlockT>::Hash) -> blockchain::Result<()> {
793+
Ok(())
794+
}
795+
796+
fn unpin_block(&self, _: <Block as BlockT>::Hash) {}
791797
}
792798

793799
impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> where Block::Hash: Ord {}

client/db/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ parity-db = "0.4.2"
2626
parking_lot = "0.12.1"
2727
sc-client-api = { version = "4.0.0-dev", path = "../api" }
2828
sc-state-db = { version = "0.10.0-dev", path = "../state-db" }
29+
schnellru = "0.2.1"
2930
sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" }
3031
sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" }
3132
sp-core = { version = "7.0.0", path = "../../primitives/core" }

0 commit comments

Comments
 (0)