github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/src/database/lmdb.rs (about) 1 /* 2 * Copyright 2018 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * ------------------------------------------------------------------------------ 16 */ 17 18 use std::collections::HashMap; 19 use std::path::Path; 20 use std::sync::Arc; 21 22 use lmdb_zero as lmdb; 23 24 use database::database::DatabaseError; 25 26 const DEFAULT_SIZE: usize = 1 << 40; // 1024 ** 4 27 28 #[derive(Clone)] 29 pub struct LmdbContext { 30 pub env: Arc<lmdb::Environment>, 31 } 32 33 impl LmdbContext { 34 pub fn new( 35 filepath: &Path, 36 indexes: usize, 37 size: Option<usize>, 38 ) -> Result<Self, DatabaseError> { 39 let flags = lmdb::open::MAPASYNC 40 | lmdb::open::WRITEMAP 41 | lmdb::open::NORDAHEAD 42 | lmdb::open::NOSUBDIR; 43 44 let filepath_str = filepath 45 .to_str() 46 .ok_or_else(|| DatabaseError::InitError(format!("Invalid filepath: {:?}", filepath)))?; 47 48 let mut builder = lmdb::EnvBuilder::new().map_err(|err| { 49 DatabaseError::InitError(format!("Failed to initialize environment: {}", err)) 50 })?; 51 builder 52 .set_maxdbs((indexes + 1) as u32) 53 .map_err(|err| DatabaseError::InitError(format!("Failed to set MAX_DBS: {}", err)))?; 54 builder 55 .set_mapsize(size.unwrap_or(DEFAULT_SIZE)) 56 .map_err(|err| DatabaseError::InitError(format!("Failed to set MAP_SIZE: {}", err)))?; 57 58 let env = unsafe { 59 builder 60 .open(filepath_str, flags, 0o600) 61 .map_err(|err| DatabaseError::InitError(format!("Database not found: {}", err))) 62 }?; 63 Ok(LmdbContext { env: Arc::new(env) }) 64 } 65 } 66 67 #[derive(Clone)] 68 pub struct LmdbDatabase { 69 ctx: LmdbContext, 70 main: Arc<lmdb::Database<'static>>, 71 indexes: Arc<HashMap<String, lmdb::Database<'static>>>, 72 } 73 74 impl LmdbDatabase { 75 pub fn new<S: AsRef<str>>(ctx: LmdbContext, indexes: &[S]) -> Result<Self, DatabaseError> { 76 let main = lmdb::Database::open( 77 ctx.env.clone(), 78 Some("main"), 79 &lmdb::DatabaseOptions::new(lmdb::db::CREATE), 80 ).map_err(|err| { 81 DatabaseError::InitError(format!("Failed to open database: {:?}", err)) 82 })?; 83 84 let mut index_dbs = HashMap::with_capacity(indexes.len()); 85 for name in indexes { 86 let db = lmdb::Database::open( 87 ctx.env.clone(), 88 Some(name.as_ref()), 89 &lmdb::DatabaseOptions::new(lmdb::db::CREATE), 90 ).map_err(|err| { 91 DatabaseError::InitError(format!("Failed to open database: {:?}", err)) 92 })?; 93 index_dbs.insert(String::from(name.as_ref()), db); 94 } 95 Ok(LmdbDatabase { 96 ctx, 97 main: Arc::new(main), 98 indexes: Arc::new(index_dbs), 99 }) 100 } 101 102 pub fn reader(&self) -> Result<LmdbDatabaseReader, DatabaseError> { 103 let txn = lmdb::ReadTransaction::new(self.ctx.env.clone()).map_err(|err| { 104 DatabaseError::ReaderError(format!("Failed to create reader: {}", err)) 105 })?; 106 Ok(LmdbDatabaseReader { db: self, txn }) 107 } 108 109 pub fn writer(&self) -> Result<LmdbDatabaseWriter, DatabaseError> { 110 let txn = lmdb::WriteTransaction::new(self.ctx.env.clone()).map_err(|err| { 111 DatabaseError::WriterError(format!("Failed to create writer: {}", err)) 112 })?; 113 Ok(LmdbDatabaseWriter { db: self, txn }) 114 } 115 } 116 117 /// A DatabaseReader provides read access to a database instance. 118 pub trait DatabaseReader { 119 /// Returns the bytes stored at the given key, if found. 120 fn get(&self, key: &[u8]) -> Option<Vec<u8>>; 121 122 /// Returns the bytes stored at the given key on a specified index, if found. 123 fn index_get(&self, index: &str, key: &[u8]) -> Result<Option<Vec<u8>>, DatabaseError>; 124 125 /// Returns a cursor against the main database. The cursor iterates over 126 /// the entries in the natural key order. 127 fn cursor(&self) -> Result<LmdbDatabaseReaderCursor, DatabaseError>; 128 129 /// Returns a cursor against the given index. The cursor iterates over 130 /// the entries in the index's natural key order. 131 fn index_cursor(&self, index: &str) -> Result<LmdbDatabaseReaderCursor, DatabaseError>; 132 133 /// Returns the number of entries in the main database. 134 fn count(&self) -> Result<usize, DatabaseError>; 135 136 /// Returns the number of entries in the given index. 137 fn index_count(&self, index: &str) -> Result<usize, DatabaseError>; 138 } 139 140 pub struct LmdbDatabaseReader<'a> { 141 db: &'a LmdbDatabase, 142 txn: lmdb::ReadTransaction<'a>, 143 } 144 145 impl<'a> DatabaseReader for LmdbDatabaseReader<'a> { 146 fn get(&self, key: &[u8]) -> Option<Vec<u8>> { 147 let access = self.txn.access(); 148 let val: Result<&[u8], _> = access.get(&self.db.main, key); 149 val.ok().map(Vec::from) 150 } 151 152 fn index_get(&self, index: &str, key: &[u8]) -> Result<Option<Vec<u8>>, DatabaseError> { 153 let index = self.db 154 .indexes 155 .get(index) 156 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 157 let access = self.txn.access(); 158 let val: Result<&[u8], _> = access.get(index, key); 159 Ok(val.ok().map(Vec::from)) 160 } 161 162 fn cursor(&self) -> Result<LmdbDatabaseReaderCursor, DatabaseError> { 163 let cursor = self.txn 164 .cursor(self.db.main.clone()) 165 .map_err(|err| DatabaseError::ReaderError(format!("{}", err)))?; 166 let access = self.txn.access(); 167 Ok(LmdbDatabaseReaderCursor { access, cursor }) 168 } 169 170 fn index_cursor(&self, index: &str) -> Result<LmdbDatabaseReaderCursor, DatabaseError> { 171 let index = self.db 172 .indexes 173 .get(index) 174 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 175 let cursor = self.txn 176 .cursor(index) 177 .map_err(|err| DatabaseError::ReaderError(format!("{}", err)))?; 178 let access = self.txn.access(); 179 Ok(LmdbDatabaseReaderCursor { access, cursor }) 180 } 181 182 fn count(&self) -> Result<usize, DatabaseError> { 183 self.txn 184 .db_stat(&self.db.main) 185 .map_err(|err| { 186 DatabaseError::CorruptionError(format!("Failed to get database stats: {}", err)) 187 }) 188 .map(|stat| stat.entries) 189 } 190 191 fn index_count(&self, index: &str) -> Result<usize, DatabaseError> { 192 let index = self.db 193 .indexes 194 .get(index) 195 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 196 self.txn 197 .db_stat(index) 198 .map_err(|err| { 199 DatabaseError::CorruptionError(format!("Failed to get database stats: {}", err)) 200 }) 201 .map(|stat| stat.entries) 202 } 203 } 204 205 pub struct LmdbDatabaseReaderCursor<'a> { 206 access: lmdb::ConstAccessor<'a>, 207 cursor: lmdb::Cursor<'a, 'a>, 208 } 209 210 impl<'a> LmdbDatabaseReaderCursor<'a> { 211 pub fn first(&mut self) -> Option<(Vec<u8>, Vec<u8>)> { 212 self.cursor 213 .first(&self.access) 214 .ok() 215 .map(|(key, value): (&[u8], &[u8])| (Vec::from(key), Vec::from(value))) 216 } 217 218 pub fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> { 219 self.cursor 220 .next(&self.access) 221 .ok() 222 .map(|(key, value): (&[u8], &[u8])| (Vec::from(key), Vec::from(value))) 223 } 224 225 pub fn last(&mut self) -> Option<(Vec<u8>, Vec<u8>)> { 226 self.cursor 227 .last(&self.access) 228 .ok() 229 .map(|(key, value): (&[u8], &[u8])| (Vec::from(key), Vec::from(value))) 230 } 231 } 232 233 pub struct LmdbDatabaseWriter<'a> { 234 db: &'a LmdbDatabase, 235 txn: lmdb::WriteTransaction<'a>, 236 } 237 238 impl<'a> LmdbDatabaseWriter<'a> { 239 /// Writes the given key/value pair. If the key/value pair already exists, 240 /// it will return a DatabaseError::DuplicateEntry. 241 pub fn put(&mut self, key: &[u8], value: &[u8]) -> Result<(), DatabaseError> { 242 self.txn 243 .access() 244 .put(&self.db.main, key, value, lmdb::put::NOOVERWRITE) 245 .map_err(|err| match err { 246 lmdb::error::Error::Code(lmdb::error::KEYEXIST) => DatabaseError::DuplicateEntry, 247 _ => DatabaseError::WriterError(format!("{}", err)), 248 }) 249 } 250 251 pub fn overwrite(&mut self, key: &[u8], value: &[u8]) -> Result<(), DatabaseError> { 252 self.txn 253 .access() 254 .put(&self.db.main, key, value, lmdb::put::Flags::empty()) 255 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 256 } 257 258 pub fn delete(&mut self, key: &[u8]) -> Result<(), DatabaseError> { 259 self.txn 260 .access() 261 .del_key(&self.db.main, key) 262 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 263 } 264 265 pub fn index_put( 266 &mut self, 267 index: &str, 268 key: &[u8], 269 value: &[u8], 270 ) -> Result<(), DatabaseError> { 271 let index = self.db 272 .indexes 273 .get(index) 274 .ok_or_else(|| DatabaseError::WriterError(format!("Not an index: {}", index)))?; 275 self.txn 276 .access() 277 .put(index, key, value, lmdb::put::Flags::empty()) 278 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 279 } 280 281 pub fn index_delete(&mut self, index: &str, key: &[u8]) -> Result<(), DatabaseError> { 282 let index = self.db 283 .indexes 284 .get(index) 285 .ok_or_else(|| DatabaseError::WriterError(format!("Not an index: {}", index)))?; 286 self.txn 287 .access() 288 .del_key(index, key) 289 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 290 } 291 292 pub fn commit(self) -> Result<(), DatabaseError> { 293 self.txn 294 .commit() 295 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 296 } 297 } 298 299 impl<'a> DatabaseReader for LmdbDatabaseWriter<'a> { 300 fn get(&self, key: &[u8]) -> Option<Vec<u8>> { 301 let access = self.txn.access(); 302 let val: Result<&[u8], _> = access.get(&self.db.main, key); 303 val.ok().map(Vec::from) 304 } 305 306 fn index_get(&self, index: &str, key: &[u8]) -> Result<Option<Vec<u8>>, DatabaseError> { 307 let index = self.db 308 .indexes 309 .get(index) 310 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 311 let access = self.txn.access(); 312 let val: Result<&[u8], _> = access.get(index, key); 313 Ok(val.ok().map(Vec::from)) 314 } 315 316 fn cursor(&self) -> Result<LmdbDatabaseReaderCursor, DatabaseError> { 317 let cursor = self.txn 318 .cursor(self.db.main.clone()) 319 .map_err(|err| DatabaseError::ReaderError(format!("{}", err)))?; 320 let access = (*self.txn).access(); 321 Ok(LmdbDatabaseReaderCursor { access, cursor }) 322 } 323 324 fn index_cursor(&self, index: &str) -> Result<LmdbDatabaseReaderCursor, DatabaseError> { 325 let index = self.db 326 .indexes 327 .get(index) 328 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 329 let cursor = self.txn 330 .cursor(index) 331 .map_err(|err| DatabaseError::ReaderError(format!("{}", err)))?; 332 let access = (*self.txn).access(); 333 Ok(LmdbDatabaseReaderCursor { access, cursor }) 334 } 335 336 fn count(&self) -> Result<usize, DatabaseError> { 337 self.txn 338 .db_stat(&self.db.main) 339 .map_err(|err| { 340 DatabaseError::CorruptionError(format!("Failed to get database stats: {}", err)) 341 }) 342 .map(|stat| stat.entries) 343 } 344 345 fn index_count(&self, index: &str) -> Result<usize, DatabaseError> { 346 let index = self.db 347 .indexes 348 .get(index) 349 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 350 self.txn 351 .db_stat(index) 352 .map_err(|err| { 353 DatabaseError::CorruptionError(format!("Failed to get database stats: {}", err)) 354 }) 355 .map(|stat| stat.entries) 356 } 357 } 358 359 #[cfg(test)] 360 mod tests { 361 use super::*; 362 use std::env; 363 use std::fs::remove_file; 364 use std::panic; 365 use std::path::Path; 366 use std::thread; 367 368 /// Asserts that there are COUNT many objects in DB. 369 fn assert_database_count(count: usize, db: &LmdbDatabase) { 370 let reader = db.reader().unwrap(); 371 372 assert_eq!(reader.count().unwrap(), count,); 373 } 374 375 /// Asserts that there are are COUNT many objects in DB's INDEX. 376 fn assert_index_count(index: &str, count: usize, db: &LmdbDatabase) { 377 let reader = db.reader().unwrap(); 378 379 assert_eq!(reader.index_count(index).unwrap(), count,); 380 } 381 382 /// Asserts that KEY is associated with VAL in DB. 383 fn assert_key_value(key: u8, val: u8, db: &LmdbDatabase) { 384 let reader = db.reader().unwrap(); 385 386 assert_eq!(reader.get(&[key]).unwrap(), [val],); 387 } 388 389 /// Asserts that KEY is associated with VAL in DB's INDEX. 390 fn assert_index_key_value(index: &str, key: u8, val: u8, db: &LmdbDatabase) { 391 let reader = db.reader().unwrap(); 392 393 assert_eq!(reader.index_get(index, &[key]).unwrap().unwrap(), [val],); 394 } 395 396 /// Asserts that KEY is not in DB. 397 fn assert_not_in_database(key: u8, db: &LmdbDatabase) { 398 let reader = db.reader().unwrap(); 399 400 assert!(reader.get(&[key]).is_none()); 401 } 402 403 /// Asserts that KEY is not in DB's INDEX. 404 fn assert_not_in_index(index: &str, key: u8, db: &LmdbDatabase) { 405 let reader = db.reader().unwrap(); 406 407 assert!(reader.index_get(index, &[key]).unwrap().is_none()); 408 } 409 410 /// Opens an LmdbDatabase and executes its basic operations 411 /// (adding keys, deleting keys, etc), making assertions about the 412 /// database contents at each step. 413 #[test] 414 fn test_lmdb() { 415 run_test(|blockstore_path| { 416 let ctx = LmdbContext::new(Path::new(blockstore_path), 3, Some(1024 * 1024)) 417 .map_err(|err| DatabaseError::InitError(format!("{}", err))) 418 .unwrap(); 419 420 let database = LmdbDatabase::new(ctx, &["a", "b"]) 421 .map_err(|err| DatabaseError::InitError(format!("{}", err))) 422 .unwrap(); 423 424 assert_database_count(0, &database); 425 assert_not_in_database(3, &database); 426 assert_not_in_database(5, &database); 427 428 // Add {3: 4} 429 let mut writer = database.writer().unwrap(); 430 writer.put(&[3], &[4]).unwrap(); 431 432 assert_database_count(0, &database); 433 assert_not_in_database(3, &database); 434 435 writer.commit().unwrap(); 436 437 assert_database_count(1, &database); 438 assert_key_value(3, 4, &database); 439 440 // Add {5: 6} 441 let mut writer = database.writer().unwrap(); 442 writer.put(&[5], &[6]).unwrap(); 443 writer.commit().unwrap(); 444 445 assert_database_count(2, &database); 446 assert_key_value(5, 6, &database); 447 assert_key_value(3, 4, &database); 448 449 // Delete {3: 4} 450 let mut writer = database.writer().unwrap(); 451 writer.delete(&[3]).unwrap(); 452 453 assert_database_count(2, &database); 454 455 writer.commit().unwrap(); 456 457 assert_database_count(1, &database); 458 assert_key_value(5, 6, &database); 459 assert_not_in_database(3, &database); 460 461 // Add {55: 5} in "a" 462 assert_index_count("a", 0, &database); 463 assert_index_count("b", 0, &database); 464 assert_not_in_index("a", 5, &database); 465 assert_not_in_index("b", 5, &database); 466 467 let mut writer = database.writer().unwrap(); 468 writer.index_put("a", &[55], &[5]).unwrap(); 469 470 assert_index_count("a", 0, &database); 471 assert_index_count("b", 0, &database); 472 assert_not_in_index("a", 5, &database); 473 assert_not_in_index("b", 5, &database); 474 475 writer.commit().unwrap(); 476 477 assert_index_count("a", 1, &database); 478 assert_index_count("b", 0, &database); 479 assert_index_key_value("a", 55, 5, &database); 480 assert_not_in_index("b", 5, &database); 481 assert_database_count(1, &database); 482 assert_key_value(5, 6, &database); 483 assert_not_in_database(3, &database); 484 485 // Delete {55: 5} in "a" 486 let mut writer = database.writer().unwrap(); 487 writer.index_delete("a", &[55]).unwrap(); 488 489 assert_index_count("a", 1, &database); 490 assert_index_count("b", 0, &database); 491 assert_index_key_value("a", 55, 5, &database); 492 assert_not_in_index("b", 5, &database); 493 494 writer.commit().unwrap(); 495 496 assert_index_count("a", 0, &database); 497 assert_index_count("b", 0, &database); 498 assert_not_in_index("a", 5, &database); 499 assert_not_in_index("b", 5, &database); 500 assert_database_count(1, &database); 501 assert_key_value(5, 6, &database); 502 assert_not_in_database(3, &database); 503 }) 504 } 505 506 fn run_test<T>(test: T) -> () 507 where 508 T: FnOnce(&str) -> () + panic::UnwindSafe, 509 { 510 let dbpath = temp_db_path(); 511 512 let testpath = dbpath.clone(); 513 let result = panic::catch_unwind(move || test(&testpath)); 514 515 remove_file(dbpath).unwrap(); 516 517 assert!(result.is_ok()) 518 } 519 520 fn temp_db_path() -> String { 521 let mut temp_dir = env::temp_dir(); 522 523 let thread_id = thread::current().id(); 524 temp_dir.push(format!("merkle-{:?}.lmdb", thread_id)); 525 temp_dir.to_str().unwrap().to_string() 526 } 527 }