Skip to content

Commit 110e0e1

Browse files
authored
Migrate from lmdb-zero to heed (#3825)
* db: migrate from lmdb-zero to heed * fix: check resizing operation and wait to avoid crash with multiple batches, fix exists check at batch * build: fix missing deps at Cargo.lock * lmdb: single environment, migrate existing databases with provided non-default environment name * fix: revert chunk size to 128mb * lmdb: ability to use multiple shared environments * build: remove unused dependency * fix: resize to have correct multiplier of the system page size * lmdb: speed up prefix iter by storing keys * lmdb: default env name * lmdb: wait db resize before read, reduce timeout before resizing * lmdb: use static reader for iterator, count existing batches for stable resize * fix: check batches count on resize waiting * lmdb: use separate databases instead of prefixes, use default db for values without prefixes, migrate old environment * fix: pop pos key * lmdb: count all open transactions to finish before resizing * lmdb: immediate resize if there are no open transactions * lmdb: remove env state when there are no more stores * lmdb: use atomic for resize and resize checking flags * lmdb: sleep 10ms when waiting all opened txs to be closed * lmdb: use atomic open txs and stores count * lmdb: use index to detect separator, ignore unknown db key to not have a panic * lmdb: store max 10k keys in the iterator * lmdb: check iter result on getting total * lmdb: handle errors at iterator * lmdb: handle an error when db with provided key not found * lmdb: fix iterate over 10k keys * lmdb: document migration resize safety * lmdb: fix iter test * lmdb: clear new db after unsuccessful migration, handle read error on migration to interrupt process * store: bring back old key methods to reproduce data migration * lmdb: return an error on unsuccessful migration * lmdb: migration test, clean data after allocate test * lmdb: info migration log * fix: move iterator before handling an error to allow skip bad value * lmdb: return an error if removal of old DB file failed after migration * lmdb: lifetime for iterator, use write transaction at batch iterator * lmdb: migration progress * fix: tests * lmdb: immediately set resizing flag, ignore resizing flag while there are more than 0 opened txs to avoid stuck, optimize tx counter for some operations * lmdb: key for successful migration * lmdb: fix put database creation at separate block to avoid lifetime issues when returning an error on migration
1 parent 151e74c commit 110e0e1

29 files changed

Lines changed: 1654 additions & 744 deletions

Cargo.lock

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

chain/src/chain.rs

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use std::collections::HashMap;
4343
use std::fs::{self, File};
4444
use std::path::{Path, PathBuf};
4545
use std::sync::atomic::{AtomicUsize, Ordering};
46-
use std::sync::Arc;
46+
use std::sync::{mpsc, Arc};
4747
use std::time::{Duration, Instant};
4848

4949
/// Orphan pool size is limited by MAX_ORPHAN_SIZE
@@ -150,8 +150,8 @@ pub struct Chain {
150150
store: Arc<store::ChainStore>,
151151
adapter: Arc<dyn ChainAdapter + Send + Sync>,
152152
orphans: Arc<OrphanBlockPool>,
153-
txhashset: Arc<RwLock<txhashset::TxHashSet>>,
154-
header_pmmr: Arc<RwLock<txhashset::PMMRHandle<BlockHeader>>>,
153+
txhashset: Arc<RwLock<TxHashSet>>,
154+
header_pmmr: Arc<RwLock<PMMRHandle<BlockHeader>>>,
155155
pibd_segmenter: Arc<RwLock<Option<Segmenter>>>,
156156
pibd_desegmenter: Arc<RwLock<Option<Desegmenter>>>,
157157
// POW verification function
@@ -171,8 +171,9 @@ impl Chain {
171171
genesis: Block,
172172
pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>,
173173
archive_mode: bool,
174+
db_migration_prog_tx: Option<mpsc::Sender<i8>>,
174175
) -> Result<Chain, Error> {
175-
let store = Arc::new(store::ChainStore::new(&db_root)?);
176+
let store = Arc::new(store::ChainStore::new(&db_root, db_migration_prog_tx)?);
176177

177178
// open the txhashset, creating a new one if necessary
178179
let mut txhashset = txhashset::TxHashSet::open(db_root.clone(), store.clone(), None)?;
@@ -189,9 +190,9 @@ impl Chain {
189190
// Initialize the output_pos index based on UTXO set
190191
// and NRD kernel_pos index based recent kernel history.
191192
{
192-
let batch = store.batch()?;
193-
txhashset.init_output_pos_index(&header_pmmr, &batch)?;
194-
txhashset.init_recent_kernel_pos_index(&header_pmmr, &batch)?;
193+
let mut batch = store.batch()?;
194+
txhashset.init_output_pos_index(&header_pmmr, &mut batch)?;
195+
txhashset.init_recent_kernel_pos_index(&header_pmmr, &mut batch)?;
195196
batch.commit()?;
196197
}
197198

@@ -275,7 +276,7 @@ impl Chain {
275276
pub fn reset_chain_head_to_genesis(&self) -> Result<(), Error> {
276277
let mut header_pmmr = self.header_pmmr.write();
277278
let mut txhashset = self.txhashset.write();
278-
let batch = self.store.batch()?;
279+
let mut batch = self.store.batch()?;
279280

280281
// Change head back to genesis
281282
{
@@ -314,7 +315,7 @@ impl Chain {
314315

315316
/// Reset PIBD head
316317
pub fn reset_pibd_head(&self) -> Result<(), Error> {
317-
let batch = self.store.batch()?;
318+
let mut batch = self.store.batch()?;
318319
batch.save_pibd_head(&self.genesis().into())?;
319320
Ok(())
320321
}
@@ -530,9 +531,9 @@ impl Chain {
530531
pub fn new_ctx<'a>(
531532
&self,
532533
opts: Options,
533-
batch: store::Batch<'a>,
534-
header_pmmr: &'a mut txhashset::PMMRHandle<BlockHeader>,
535-
txhashset: &'a mut txhashset::TxHashSet,
534+
batch: Batch<'a>,
535+
header_pmmr: &'a mut PMMRHandle<BlockHeader>,
536+
txhashset: &'a mut TxHashSet,
536537
) -> Result<pipe::BlockContext<'a>, Error> {
537538
let denylist = self.denylist.read().clone();
538539
Ok(pipe::BlockContext {
@@ -832,7 +833,7 @@ impl Chain {
832833
&self,
833834
header: &BlockHeader,
834835
ext: &mut ExtensionPair,
835-
batch: &Batch,
836+
batch: &mut Batch,
836837
) -> Result<BlockHeader, Error> {
837838
let denylist = self.denylist.read().clone();
838839
pipe::rewind_and_apply_fork(header, ext, batch, &|header| {
@@ -846,7 +847,7 @@ impl Chain {
846847
&self,
847848
header: &BlockHeader,
848849
ext: &mut HeaderExtension,
849-
batch: &Batch,
850+
batch: &mut Batch,
850851
) -> Result<(), Error> {
851852
let denylist = self.denylist.read().clone();
852853
pipe::rewind_and_apply_header_fork(header, ext, batch, &|header| {
@@ -1015,7 +1016,7 @@ impl Chain {
10151016
fn validate_kernel_history(
10161017
&self,
10171018
header: &BlockHeader,
1018-
txhashset: &txhashset::TxHashSet,
1019+
txhashset: &TxHashSet,
10191020
) -> Result<(), Error> {
10201021
debug!("validate_kernel_history: rewinding and validating kernel history (readonly)");
10211022

@@ -1151,11 +1152,11 @@ impl Chain {
11511152
self.validate_kernel_history(&header, &txhashset)?;
11521153

11531154
let header_pmmr = self.header_pmmr.read();
1154-
let batch = self.store.batch()?;
1155+
let mut batch = self.store.batch()?;
11551156
txhashset.verify_kernel_pos_index(
11561157
&self.genesis.header,
11571158
&header_pmmr,
1158-
&batch,
1159+
&mut batch,
11591160
None,
11601161
None,
11611162
)?;
@@ -1213,10 +1214,10 @@ impl Chain {
12131214
}
12141215

12151216
// Rebuild our output_pos index in the db based on fresh UTXO set.
1216-
txhashset.init_output_pos_index(&header_pmmr, &batch)?;
1217+
txhashset.init_output_pos_index(&header_pmmr, &mut batch)?;
12171218

12181219
// Rebuild our NRD kernel_pos index based on recent kernel history.
1219-
txhashset.init_recent_kernel_pos_index(&header_pmmr, &batch)?;
1220+
txhashset.init_recent_kernel_pos_index(&header_pmmr, &mut batch)?;
12201221

12211222
// Commit all the changes to the db.
12221223
batch.commit()?;
@@ -1257,9 +1258,9 @@ impl Chain {
12571258
/// *Only* runs if we are not in archive mode.
12581259
fn remove_historical_blocks(
12591260
&self,
1260-
header_pmmr: &txhashset::PMMRHandle<BlockHeader>,
1261+
header_pmmr: &PMMRHandle<BlockHeader>,
12611262
archive_header: BlockHeader,
1262-
batch: &store::Batch<'_>,
1263+
batch: &mut Batch<'_>,
12631264
) -> Result<(), Error> {
12641265
if self.archive_mode() {
12651266
return Ok(());
@@ -1293,17 +1294,24 @@ impl Chain {
12931294
return Ok(());
12941295
}
12951296

1296-
let mut count = 0;
12971297
let tail_hash = header_pmmr.get_header_hash_by_height(head.height - horizon)?;
12981298
let tail = batch.get_block_header(&tail_hash)?;
12991299

1300-
// Remove old blocks (including short lived fork blocks) which height < tail.height
1301-
for block in batch.blocks_iter()? {
1302-
if block.header.height < tail.height {
1303-
let _ = batch.delete_block(&block.hash());
1304-
count += 1;
1300+
// Remove old blocks (including short-lived fork blocks) which height < tail.height
1301+
let mut blocks_to_delete = vec![];
1302+
let iter = batch.blocks_iter()?;
1303+
for block in iter {
1304+
if let Ok(block) = block {
1305+
if block.header.height < tail.height {
1306+
blocks_to_delete.push(block.hash());
1307+
}
13051308
}
13061309
}
1310+
let mut count = 0;
1311+
for bh in blocks_to_delete {
1312+
let _ = batch.delete_block(&bh);
1313+
count += 1;
1314+
}
13071315

13081316
batch.save_body_tail(&Tip::from_header(&tail))?;
13091317

@@ -1345,7 +1353,7 @@ impl Chain {
13451353
// Take a write lock on the txhashet and start a new writeable db batch.
13461354
let header_pmmr = self.header_pmmr.read();
13471355
let mut txhashset = self.txhashset.write();
1348-
let batch = self.store.batch()?;
1356+
let mut batch = self.store.batch()?;
13491357

13501358
// Compact the txhashset itself (rewriting the pruned backend files).
13511359
{
@@ -1361,15 +1369,15 @@ impl Chain {
13611369

13621370
// If we are not in archival mode remove historical blocks from the db.
13631371
if !self.archive_mode() {
1364-
self.remove_historical_blocks(&header_pmmr, archive_header, &batch)?;
1372+
self.remove_historical_blocks(&header_pmmr, archive_header, &mut batch)?;
13651373
}
13661374

13671375
// Make sure our output_pos index is consistent with the UTXO set.
1368-
txhashset.init_output_pos_index(&header_pmmr, &batch)?;
1376+
txhashset.init_output_pos_index(&header_pmmr, &mut batch)?;
13691377

13701378
// TODO - Why is this part of chain compaction?
13711379
// Rebuild our NRD kernel_pos index based on recent kernel history.
1372-
txhashset.init_recent_kernel_pos_index(&header_pmmr, &batch)?;
1380+
txhashset.init_recent_kernel_pos_index(&header_pmmr, &mut batch)?;
13731381

13741382
// Commit all the above db changes.
13751383
batch.commit()?;

0 commit comments

Comments
 (0)