// Copyright 2020 Parity Technologies (UK) Ltd. // 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 keyvaluedb::KeyValueDB; use keyvaluedb_sqlite::{Database, DatabaseConfig}; use std::sync::{atomic::AtomicBool, atomic::Ordering as AtomicOrdering, Arc}; use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; const COLUMN_COUNT: u32 = 100; #[derive(Clone)] #[allow(dead_code)] struct KeyValueSeed { seed: H256, 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 KeyValueSeed { fn with_seed(seed: H256) -> Self { KeyValueSeed { seed, key: next(seed), val: next(next(seed)), } } fn new() -> Self { Self::with_seed(H256::random()) } } impl Iterator for KeyValueSeed { 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 } #[tokio::main] async fn main() { 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 config = DatabaseConfig::with_columns(COLUMN_COUNT); let dir = tempfile::Builder::new() .prefix("sqlite-example") .tempdir() .unwrap(); println!( "Database is put in: {:?} (maybe check if it was deleted)", dir.path() ); let db = Database::open(dir.path(), config).unwrap(); let mut step = 0; let mut keyvalues = KeyValueSeed::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).await.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()) .await .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).await.expect("delete failed"); keyvalues = KeyValueSeed::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; } }