2019-07-20 13:13:55 -07:00
|
|
|
//! The `ledger_cleanup_service` drops older ledger data to limit disk space usage
|
|
|
|
|
2020-01-13 13:13:52 -08:00
|
|
|
use solana_ledger::blockstore::Blockstore;
|
2019-12-06 19:32:45 -08:00
|
|
|
use solana_metrics::datapoint_debug;
|
2019-12-18 11:50:09 -08:00
|
|
|
use solana_sdk::clock::Slot;
|
2019-07-20 13:13:55 -07:00
|
|
|
use std::string::ToString;
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::mpsc::{Receiver, RecvTimeoutError};
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::thread;
|
|
|
|
use std::thread::{Builder, JoinHandle};
|
|
|
|
use std::time::Duration;
|
|
|
|
|
2019-12-18 11:50:09 -08:00
|
|
|
// This is chosen to allow enough time for
|
2020-02-21 15:04:02 -08:00
|
|
|
// - To try and keep the RocksDB size under 512GB at 50k tps (100 slots take ~2GB).
|
2019-12-18 11:50:09 -08:00
|
|
|
// - A validator to download a snapshot from a peer and boot from it
|
|
|
|
// - To make sure that if a validator needs to reboot from its own snapshot, it has enough slots locally
|
|
|
|
// to catch back up to where it was when it stopped
|
2020-02-21 15:04:02 -08:00
|
|
|
pub const DEFAULT_MAX_LEDGER_SLOTS: u64 = 270_000;
|
2019-12-18 11:50:09 -08:00
|
|
|
// Remove a fixed number of slots at a time, it's more efficient than doing it one-by-one
|
|
|
|
pub const DEFAULT_PURGE_BATCH_SIZE: u64 = 256;
|
2019-07-20 13:13:55 -07:00
|
|
|
|
|
|
|
pub struct LedgerCleanupService {
|
|
|
|
t_cleanup: JoinHandle<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LedgerCleanupService {
|
|
|
|
pub fn new(
|
2019-12-18 11:50:09 -08:00
|
|
|
new_root_receiver: Receiver<Slot>,
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore: Arc<Blockstore>,
|
2019-07-20 13:13:55 -07:00
|
|
|
max_ledger_slots: u64,
|
|
|
|
exit: &Arc<AtomicBool>,
|
|
|
|
) -> Self {
|
2019-07-24 17:28:08 -07:00
|
|
|
info!(
|
|
|
|
"LedgerCleanupService active. Max Ledger Slots {}",
|
|
|
|
max_ledger_slots
|
|
|
|
);
|
2019-07-20 13:13:55 -07:00
|
|
|
let exit = exit.clone();
|
2019-12-18 11:50:09 -08:00
|
|
|
let mut next_purge_batch = max_ledger_slots;
|
2019-07-20 13:13:55 -07:00
|
|
|
let t_cleanup = Builder::new()
|
|
|
|
.name("solana-ledger-cleanup".to_string())
|
|
|
|
.spawn(move || loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
2019-12-18 11:50:09 -08:00
|
|
|
if let Err(e) = Self::cleanup_ledger(
|
|
|
|
&new_root_receiver,
|
2020-01-13 13:13:52 -08:00
|
|
|
&blockstore,
|
2019-12-18 11:50:09 -08:00
|
|
|
max_ledger_slots,
|
|
|
|
&mut next_purge_batch,
|
|
|
|
) {
|
2019-07-20 13:13:55 -07:00
|
|
|
match e {
|
2020-01-02 19:50:43 -08:00
|
|
|
RecvTimeoutError::Disconnected => break,
|
|
|
|
RecvTimeoutError::Timeout => (),
|
2019-07-20 13:13:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
Self { t_cleanup }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cleanup_ledger(
|
2019-12-18 11:50:09 -08:00
|
|
|
new_root_receiver: &Receiver<Slot>,
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore: &Arc<Blockstore>,
|
2019-07-20 13:13:55 -07:00
|
|
|
max_ledger_slots: u64,
|
2019-12-18 11:50:09 -08:00
|
|
|
next_purge_batch: &mut u64,
|
2020-01-02 19:50:43 -08:00
|
|
|
) -> Result<(), RecvTimeoutError> {
|
2020-01-13 13:13:52 -08:00
|
|
|
let disk_utilization_pre = blockstore.storage_size();
|
2019-12-06 19:32:45 -08:00
|
|
|
|
2019-12-18 11:50:09 -08:00
|
|
|
let root = new_root_receiver.recv_timeout(Duration::from_secs(1))?;
|
2020-01-28 13:45:41 -08:00
|
|
|
|
|
|
|
// Notify blockstore of impending purge
|
2019-12-18 11:50:09 -08:00
|
|
|
if root > *next_purge_batch {
|
2019-07-20 13:13:55 -07:00
|
|
|
//cleanup
|
2020-01-28 13:45:41 -08:00
|
|
|
let lowest_slot = root - max_ledger_slots;
|
|
|
|
*blockstore.lowest_cleanup_slot.write().unwrap() = lowest_slot;
|
|
|
|
blockstore.purge_slots(0, Some(lowest_slot));
|
2019-12-18 11:50:09 -08:00
|
|
|
*next_purge_batch += DEFAULT_PURGE_BATCH_SIZE;
|
2019-07-20 13:13:55 -07:00
|
|
|
}
|
2019-12-06 19:32:45 -08:00
|
|
|
|
2020-01-13 13:13:52 -08:00
|
|
|
let disk_utilization_post = blockstore.storage_size();
|
2019-12-06 19:32:45 -08:00
|
|
|
|
2019-12-12 11:55:30 -08:00
|
|
|
if let (Ok(disk_utilization_pre), Ok(disk_utilization_post)) =
|
2019-12-10 16:12:49 -08:00
|
|
|
(disk_utilization_pre, disk_utilization_post)
|
|
|
|
{
|
|
|
|
datapoint_debug!(
|
|
|
|
"ledger_disk_utilization",
|
|
|
|
("disk_utilization_pre", disk_utilization_pre as i64, i64),
|
|
|
|
("disk_utilization_post", disk_utilization_post as i64, i64),
|
|
|
|
(
|
|
|
|
"disk_utilization_delta",
|
|
|
|
(disk_utilization_pre as i64 - disk_utilization_post as i64),
|
|
|
|
i64
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2019-12-06 19:32:45 -08:00
|
|
|
|
2019-07-20 13:13:55 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-11-13 10:12:09 -08:00
|
|
|
pub fn join(self) -> thread::Result<()> {
|
2019-07-20 13:13:55 -07:00
|
|
|
self.t_cleanup.join()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-01-13 13:13:52 -08:00
|
|
|
use solana_ledger::blockstore::make_many_slot_entries;
|
2019-11-13 07:14:09 -08:00
|
|
|
use solana_ledger::get_tmp_ledger_path;
|
2019-07-20 13:13:55 -07:00
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cleanup() {
|
2020-01-13 13:13:52 -08:00
|
|
|
let blockstore_path = get_tmp_ledger_path!();
|
|
|
|
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
2019-09-03 21:32:51 -07:00
|
|
|
let (shreds, _) = make_many_slot_entries(0, 50, 5);
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore.insert_shreds(shreds, None, false).unwrap();
|
|
|
|
let blockstore = Arc::new(blockstore);
|
2019-07-20 13:13:55 -07:00
|
|
|
let (sender, receiver) = channel();
|
|
|
|
|
|
|
|
//send a signal to kill slots 0-40
|
2019-12-18 11:50:09 -08:00
|
|
|
let mut next_purge_slot = 0;
|
|
|
|
sender.send(50).unwrap();
|
2020-01-13 13:13:52 -08:00
|
|
|
LedgerCleanupService::cleanup_ledger(&receiver, &blockstore, 10, &mut next_purge_slot)
|
2019-12-18 11:50:09 -08:00
|
|
|
.unwrap();
|
2019-07-20 13:13:55 -07:00
|
|
|
|
|
|
|
//check that 0-40 don't exist
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore
|
2019-07-20 13:13:55 -07:00
|
|
|
.slot_meta_iterator(0)
|
|
|
|
.unwrap()
|
|
|
|
.for_each(|(slot, _)| assert!(slot > 40));
|
|
|
|
|
2020-01-13 13:13:52 -08:00
|
|
|
drop(blockstore);
|
|
|
|
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
2019-07-20 13:13:55 -07:00
|
|
|
}
|
2019-12-18 07:29:46 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_compaction() {
|
2020-01-13 13:13:52 -08:00
|
|
|
let blockstore_path = get_tmp_ledger_path!();
|
|
|
|
let blockstore = Arc::new(Blockstore::open(&blockstore_path).unwrap());
|
2019-12-18 07:29:46 -08:00
|
|
|
|
|
|
|
let n = 10_000;
|
|
|
|
let batch_size = 100;
|
|
|
|
let batches = n / batch_size;
|
|
|
|
let max_ledger_slots = 100;
|
|
|
|
|
|
|
|
for i in 0..batches {
|
|
|
|
let (shreds, _) = make_many_slot_entries(i * batch_size, batch_size, 1);
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore.insert_shreds(shreds, None, false).unwrap();
|
2019-12-18 07:29:46 -08:00
|
|
|
}
|
|
|
|
|
2020-01-13 13:13:52 -08:00
|
|
|
let u1 = blockstore.storage_size().unwrap() as f64;
|
2019-12-18 07:29:46 -08:00
|
|
|
|
|
|
|
// send signal to cleanup slots
|
|
|
|
let (sender, receiver) = channel();
|
2019-12-18 11:50:09 -08:00
|
|
|
sender.send(n).unwrap();
|
|
|
|
let mut next_purge_batch = 0;
|
|
|
|
LedgerCleanupService::cleanup_ledger(
|
|
|
|
&receiver,
|
2020-01-13 13:13:52 -08:00
|
|
|
&blockstore,
|
2019-12-18 11:50:09 -08:00
|
|
|
max_ledger_slots,
|
|
|
|
&mut next_purge_batch,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2019-12-18 07:29:46 -08:00
|
|
|
|
|
|
|
thread::sleep(Duration::from_secs(2));
|
|
|
|
|
2020-01-13 13:13:52 -08:00
|
|
|
let u2 = blockstore.storage_size().unwrap() as f64;
|
2019-12-18 07:29:46 -08:00
|
|
|
|
|
|
|
assert!(u2 < u1, "insufficient compaction! pre={},post={}", u1, u2,);
|
|
|
|
|
|
|
|
// check that early slots don't exist
|
|
|
|
let max_slot = n - max_ledger_slots;
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore
|
2019-12-18 07:29:46 -08:00
|
|
|
.slot_meta_iterator(0)
|
|
|
|
.unwrap()
|
|
|
|
.for_each(|(slot, _)| assert!(slot > max_slot));
|
|
|
|
|
2020-01-13 13:13:52 -08:00
|
|
|
drop(blockstore);
|
|
|
|
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
2019-12-18 07:29:46 -08:00
|
|
|
}
|
2019-07-20 13:13:55 -07:00
|
|
|
}
|