556190ff46
## Issue Addressed Closes #1866 ## Proposed Changes * Compact the database on finalization. This removes the deleted states from disk completely. Because it happens in the background migrator, it doesn't block other database operations while it runs. On my Medalla node it took about 1 minute and shrank the database from 90GB to 9GB. * Fix an inefficiency in the pruning algorithm where it would always use the genesis checkpoint as the `old_finalized_checkpoint` when running for the first time after start-up. This would result in loading lots of states one-at-a-time back to genesis, and storing a lot of block roots in memory. The new code stores the old finalized checkpoint on disk and only uses genesis if no checkpoint is already stored. This makes it both backwards compatible _and_ forwards compatible -- no schema change required! * Introduce two new `INFO` logs to indicate when pruning has started and completed. Users seem to want to know this information without enabling debug logs!
94 lines
2.7 KiB
Rust
94 lines
2.7 KiB
Rust
use super::{Error, ItemStore, KeyValueStore, KeyValueStoreOp};
|
|
use parking_lot::{Mutex, MutexGuard, RwLock};
|
|
use std::collections::HashMap;
|
|
use std::marker::PhantomData;
|
|
use types::*;
|
|
|
|
type DBHashMap = HashMap<Vec<u8>, Vec<u8>>;
|
|
|
|
/// A thread-safe `HashMap` wrapper.
|
|
pub struct MemoryStore<E: EthSpec> {
|
|
db: RwLock<DBHashMap>,
|
|
transaction_mutex: Mutex<()>,
|
|
_phantom: PhantomData<E>,
|
|
}
|
|
|
|
impl<E: EthSpec> MemoryStore<E> {
|
|
/// Create a new, empty database.
|
|
pub fn open() -> Self {
|
|
Self {
|
|
db: RwLock::new(HashMap::new()),
|
|
transaction_mutex: Mutex::new(()),
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
|
|
fn get_key_for_col(col: &str, key: &[u8]) -> Vec<u8> {
|
|
let mut col = col.as_bytes().to_vec();
|
|
col.append(&mut key.to_vec());
|
|
col
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> KeyValueStore<E> for MemoryStore<E> {
|
|
/// Get the value of some key from the database. Returns `None` if the key does not exist.
|
|
fn get_bytes(&self, col: &str, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
|
let column_key = Self::get_key_for_col(col, key);
|
|
Ok(self.db.read().get(&column_key).cloned())
|
|
}
|
|
|
|
/// Puts a key in the database.
|
|
fn put_bytes(&self, col: &str, key: &[u8], val: &[u8]) -> Result<(), Error> {
|
|
let column_key = Self::get_key_for_col(col, key);
|
|
self.db.write().insert(column_key, val.to_vec());
|
|
Ok(())
|
|
}
|
|
|
|
fn put_bytes_sync(&self, col: &str, key: &[u8], val: &[u8]) -> Result<(), Error> {
|
|
self.put_bytes(col, key, val)
|
|
}
|
|
|
|
fn sync(&self) -> Result<(), Error> {
|
|
// no-op
|
|
Ok(())
|
|
}
|
|
|
|
/// Return true if some key exists in some column.
|
|
fn key_exists(&self, col: &str, key: &[u8]) -> Result<bool, Error> {
|
|
let column_key = Self::get_key_for_col(col, key);
|
|
Ok(self.db.read().contains_key(&column_key))
|
|
}
|
|
|
|
/// Delete some key from the database.
|
|
fn key_delete(&self, col: &str, key: &[u8]) -> Result<(), Error> {
|
|
let column_key = Self::get_key_for_col(col, key);
|
|
self.db.write().remove(&column_key);
|
|
Ok(())
|
|
}
|
|
|
|
fn do_atomically(&self, batch: Vec<KeyValueStoreOp>) -> Result<(), Error> {
|
|
for op in batch {
|
|
match op {
|
|
KeyValueStoreOp::PutKeyValue(key, value) => {
|
|
self.db.write().insert(key, value);
|
|
}
|
|
|
|
KeyValueStoreOp::DeleteKey(hash) => {
|
|
self.db.write().remove(&hash);
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn begin_rw_transaction(&self) -> MutexGuard<()> {
|
|
self.transaction_mutex.lock()
|
|
}
|
|
|
|
fn compact(&self) -> Result<(), Error> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<E: EthSpec> ItemStore<E> for MemoryStore<E> {}
|