// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . // This program starts writing random data to the database with 100 (COLUMN_COUNT) // columns and never stops until interrupted. use ethereum_types::H256; use keccak_hash::keccak; use kvdb_rocksdb::{Database, DatabaseConfig}; use std::sync::{ atomic::{AtomicBool, Ordering as AtomicOrdering}, Arc, }; use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; const COLUMN_COUNT: u32 = 100; #[derive(Clone)] struct KeyValue { key: H256, val: H256, } fn next(seed: H256) -> H256 { let mut buf = [0u8; 33]; buf[0..32].copy_from_slice(&seed[..]); buf[32] = 1; keccak(&buf[..]) } impl KeyValue { fn with_seed(seed: H256) -> Self { KeyValue { key: next(seed), val: next(next(seed)) } } fn new() -> Self { Self::with_seed(H256::random()) } } impl Iterator for KeyValue { type Item = (H256, H256); fn next(&mut self) -> Option { let result = (self.key, self.val); self.key = next(self.val); self.val = next(self.key); Some(result) } } fn proc_memory_usage() -> u64 { let mut sys = System::new(); let self_pid = get_current_pid().ok(); let memory = if let Some(self_pid) = self_pid { if sys.refresh_process(self_pid) { let proc = sys .process(self_pid) .expect("Above refresh_process succeeds, this should be Some(), qed"); proc.memory() } else { 0 } } else { 0 }; memory } fn main() { let mb_per_col = std::env::args() .nth(1) .map(|arg| arg.parse().expect("Megabytes per col - should be integer or missing")) .unwrap_or(1); let exit = Arc::new(AtomicBool::new(false)); let ctrlc_exit = exit.clone(); ctrlc::set_handler(move || { println!("\nRemoving temp database...\n"); ctrlc_exit.store(true, AtomicOrdering::Relaxed); }) .expect("Error setting Ctrl-C handler"); let mut config = DatabaseConfig::with_columns(COLUMN_COUNT); for c in 0..=COLUMN_COUNT { config.memory_budget.insert(c, mb_per_col); } let dir = tempfile::Builder::new().prefix("rocksdb-example").tempdir().unwrap(); println!("Database is put in: {} (maybe check if it was deleted)", dir.path().to_string_lossy()); let db = Database::open(&config, &dir.path()).unwrap(); let mut step = 0; let mut keyvalues = KeyValue::new(); while !exit.load(AtomicOrdering::Relaxed) { let col = step % 100; let key_values: Vec<(H256, H256)> = keyvalues.clone().take(128).collect(); let mut transaction = db.transaction(); for (k, v) in key_values.iter() { transaction.put(col, k.as_ref(), v.as_ref()); } db.write(transaction).expect("writing failed"); let mut seed = H256::zero(); for (k, _) in key_values.iter() { let mut buf = [0u8; 64]; buf[0..32].copy_from_slice(seed.as_ref()); let val = db.get(col, k.as_ref()).expect("Db fail").expect("Was put above"); buf[32..64].copy_from_slice(val.as_ref()); seed = keccak(&buf[..]); } let mut transaction = db.transaction(); // delete all but one to avoid too much bloating for (k, _) in key_values.iter().take(127) { transaction.delete(col, k.as_ref()); } db.write(transaction).expect("delete failed"); keyvalues = KeyValue::with_seed(seed); if step % 10000 == 9999 { let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"); println!("{}", timestamp); println!("\tData written: {} keys - {} Mb", step + 1, ((step + 1) * 64 * 128) / 1024 / 1024); println!("\tProcess memory used as seen by the OS: {} Mb", proc_memory_usage() / 1024); } step += 1; } }