github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/adm/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 21 use lmdb_zero as lmdb; 22 23 use database::error::DatabaseError; 24 25 const DEFAULT_SIZE: usize = 1 << 40; // 1024 ** 4 26 27 pub struct LmdbContext { 28 pub env: lmdb::Environment, 29 } 30 31 impl LmdbContext { 32 pub fn new(filepath: &Path, indexes: u32, size: Option<usize>) -> Result<Self, DatabaseError> { 33 let flags = lmdb::open::MAPASYNC | lmdb::open::WRITEMAP | lmdb::open::NOSUBDIR; 34 35 let filepath_str = filepath 36 .to_str() 37 .ok_or_else(|| DatabaseError::InitError(format!("Invalid filepath: {:?}", filepath)))?; 38 39 let mut builder = lmdb::EnvBuilder::new().map_err(|err| { 40 DatabaseError::InitError(format!("Failed to initialize environment: {}", err)) 41 })?; 42 builder 43 .set_maxdbs(indexes + 1) 44 .map_err(|err| DatabaseError::InitError(format!("Failed to set MAX_DBS: {}", err)))?; 45 builder 46 .set_mapsize(size.unwrap_or(DEFAULT_SIZE)) 47 .map_err(|err| DatabaseError::InitError(format!("Failed to set MAP_SIZE: {}", err)))?; 48 49 let env = unsafe { 50 builder 51 .open(filepath_str, flags, 0o600) 52 .map_err(|err| DatabaseError::InitError(format!("Database not found: {}", err))) 53 }?; 54 Ok(LmdbContext { env: env }) 55 } 56 } 57 58 pub struct LmdbDatabase<'e> { 59 ctx: &'e LmdbContext, 60 main: lmdb::Database<'e>, 61 indexes: HashMap<String, lmdb::Database<'e>>, 62 } 63 64 impl<'e> LmdbDatabase<'e> { 65 pub fn new(ctx: &'e LmdbContext, indexes: &[&str]) -> Result<Self, DatabaseError> { 66 let main = lmdb::Database::open( 67 &ctx.env, 68 Some("main"), 69 &lmdb::DatabaseOptions::new(lmdb::db::CREATE), 70 ).map_err(|err| { 71 DatabaseError::InitError(format!("Failed to open database: {:?}", err)) 72 })?; 73 74 let mut index_dbs = HashMap::with_capacity(indexes.len()); 75 for name in indexes { 76 let db = lmdb::Database::open( 77 &ctx.env, 78 Some(name), 79 &lmdb::DatabaseOptions::new(lmdb::db::CREATE), 80 ).map_err(|err| { 81 DatabaseError::InitError(format!("Failed to open database: {:?}", err)) 82 })?; 83 index_dbs.insert(String::from(*name), db); 84 } 85 Ok(LmdbDatabase { 86 ctx: ctx, 87 main: main, 88 indexes: index_dbs, 89 }) 90 } 91 92 pub fn reader(&self) -> Result<LmdbDatabaseReader, DatabaseError> { 93 let txn = lmdb::ReadTransaction::new(&self.ctx.env).map_err(|err| { 94 DatabaseError::ReaderError(format!("Failed to create reader: {}", err)) 95 })?; 96 Ok(LmdbDatabaseReader { db: self, txn: txn }) 97 } 98 99 pub fn writer(&self) -> Result<LmdbDatabaseWriter, DatabaseError> { 100 let txn = lmdb::WriteTransaction::new(&self.ctx.env).map_err(|err| { 101 DatabaseError::WriterError(format!("Failed to create writer: {}", err)) 102 })?; 103 Ok(LmdbDatabaseWriter { db: self, txn: txn }) 104 } 105 } 106 107 pub struct LmdbDatabaseReader<'a> { 108 db: &'a LmdbDatabase<'a>, 109 txn: lmdb::ReadTransaction<'a>, 110 } 111 112 impl<'a> LmdbDatabaseReader<'a> { 113 pub fn get(&self, key: &[u8]) -> Option<Vec<u8>> { 114 let access = self.txn.access(); 115 let val: Result<&[u8], _> = access.get(&self.db.main, key); 116 val.ok().map(Vec::from) 117 } 118 119 pub fn index_get(&self, index: &str, key: &[u8]) -> Result<Option<Vec<u8>>, DatabaseError> { 120 let index = self.db 121 .indexes 122 .get(index) 123 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 124 let access = self.txn.access(); 125 let val: Result<&[u8], _> = access.get(index, key); 126 Ok(val.ok().map(Vec::from)) 127 } 128 129 #[allow(dead_code)] 130 pub fn cursor(&self) -> Result<LmdbDatabaseReaderCursor, DatabaseError> { 131 let cursor = self.txn 132 .cursor(&self.db.main) 133 .map_err(|err| DatabaseError::ReaderError(format!("{}", err)))?; 134 let access = self.txn.access(); 135 Ok(LmdbDatabaseReaderCursor { 136 access: access, 137 cursor: cursor, 138 }) 139 } 140 141 pub fn index_cursor(&self, index: &str) -> Result<LmdbDatabaseReaderCursor, DatabaseError> { 142 let index = self.db 143 .indexes 144 .get(index) 145 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 146 let cursor = self.txn 147 .cursor(index) 148 .map_err(|err| DatabaseError::ReaderError(format!("{}", err)))?; 149 let access = self.txn.access(); 150 Ok(LmdbDatabaseReaderCursor { 151 access: access, 152 cursor: cursor, 153 }) 154 } 155 156 pub fn count(&self) -> Result<usize, DatabaseError> { 157 self.txn 158 .db_stat(&self.db.main) 159 .map_err(|err| { 160 DatabaseError::CorruptionError(format!("Failed to get database stats: {}", err)) 161 }) 162 .map(|stat| stat.entries) 163 } 164 165 pub fn index_count(&self, index: &str) -> Result<usize, DatabaseError> { 166 let index = self.db 167 .indexes 168 .get(index) 169 .ok_or_else(|| DatabaseError::ReaderError(format!("Not an index: {}", index)))?; 170 self.txn 171 .db_stat(index) 172 .map_err(|err| { 173 DatabaseError::CorruptionError(format!("Failed to get database stats: {}", err)) 174 }) 175 .map(|stat| stat.entries) 176 } 177 } 178 179 pub struct LmdbDatabaseReaderCursor<'a> { 180 access: lmdb::ConstAccessor<'a>, 181 cursor: lmdb::Cursor<'a, 'a>, 182 } 183 184 impl<'a> LmdbDatabaseReaderCursor<'a> { 185 #[allow(dead_code)] 186 pub fn first(&mut self) -> Option<(Vec<u8>, Vec<u8>)> { 187 self.cursor 188 .first(&self.access) 189 .ok() 190 .map(|(key, value): (&[u8], &[u8])| (Vec::from(key), Vec::from(value))) 191 } 192 193 pub fn last(&mut self) -> Option<(Vec<u8>, Vec<u8>)> { 194 self.cursor 195 .last(&self.access) 196 .ok() 197 .map(|(key, value): (&[u8], &[u8])| (Vec::from(key), Vec::from(value))) 198 } 199 } 200 201 pub struct LmdbDatabaseWriter<'a> { 202 db: &'a LmdbDatabase<'a>, 203 txn: lmdb::WriteTransaction<'a>, 204 } 205 206 impl<'a> LmdbDatabaseWriter<'a> { 207 pub fn put(&mut self, key: &[u8], value: &[u8]) -> Result<(), DatabaseError> { 208 self.txn 209 .access() 210 .put(&self.db.main, key, value, lmdb::put::Flags::empty()) 211 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 212 } 213 214 pub fn delete(&mut self, key: &[u8]) -> Result<(), DatabaseError> { 215 self.txn 216 .access() 217 .del_key(&self.db.main, key) 218 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 219 } 220 221 pub fn index_put( 222 &mut self, 223 index: &str, 224 key: &[u8], 225 value: &[u8], 226 ) -> Result<(), DatabaseError> { 227 let index = self.db 228 .indexes 229 .get(index) 230 .ok_or_else(|| DatabaseError::WriterError(format!("Not an index: {}", index)))?; 231 self.txn 232 .access() 233 .put(index, key, value, lmdb::put::Flags::empty()) 234 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 235 } 236 237 pub fn index_delete(&mut self, index: &str, key: &[u8]) -> Result<(), DatabaseError> { 238 let index = self.db 239 .indexes 240 .get(index) 241 .ok_or_else(|| DatabaseError::WriterError(format!("Not an index: {}", index)))?; 242 self.txn 243 .access() 244 .del_key(index, key) 245 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 246 } 247 248 pub fn commit(self) -> Result<(), DatabaseError> { 249 self.txn 250 .commit() 251 .map_err(|err| DatabaseError::WriterError(format!("{}", err))) 252 } 253 } 254 255 #[cfg(test)] 256 mod tests { 257 use super::*; 258 use config; 259 260 /// Asserts that there are COUNT many objects in DB. 261 fn assert_database_count(count: usize, db: &LmdbDatabase) { 262 let reader = db.reader().unwrap(); 263 264 assert_eq!(reader.count().unwrap(), count,); 265 } 266 267 /// Asserts that there are are COUNT many objects in DB's INDEX. 268 fn assert_index_count(index: &str, count: usize, db: &LmdbDatabase) { 269 let reader = db.reader().unwrap(); 270 271 assert_eq!(reader.index_count(index).unwrap(), count,); 272 } 273 274 /// Asserts that KEY is associated with VAL in DB. 275 fn assert_key_value(key: u8, val: u8, db: &LmdbDatabase) { 276 let reader = db.reader().unwrap(); 277 278 assert_eq!(reader.get(&[key]).unwrap(), [val],); 279 } 280 281 /// Asserts that KEY is associated with VAL in DB's INDEX. 282 fn assert_index_key_value(index: &str, key: u8, val: u8, db: &LmdbDatabase) { 283 let reader = db.reader().unwrap(); 284 285 assert_eq!(reader.index_get(index, &[key]).unwrap().unwrap(), [val],); 286 } 287 288 /// Asserts that KEY is not in DB. 289 fn assert_not_in_database(key: u8, db: &LmdbDatabase) { 290 let reader = db.reader().unwrap(); 291 292 assert!(reader.get(&[key]).is_none()); 293 } 294 295 /// Asserts that KEY is not in DB's INDEX. 296 fn assert_not_in_index(index: &str, key: u8, db: &LmdbDatabase) { 297 let reader = db.reader().unwrap(); 298 299 assert!(reader.index_get(index, &[key]).unwrap().is_none()); 300 } 301 302 /// Opens an LmdbDatabase and executes its basic operations 303 /// (adding keys, deleting keys, etc), making assertions about the 304 /// database contents at each step. 305 #[test] 306 fn test_lmdb() { 307 let path_config = config::get_path_config(); 308 309 let blockstore_path = &path_config.data_dir.join(String::from("unit-lmdb.lmdb")); 310 311 let ctx = LmdbContext::new(blockstore_path, 3, None) 312 .map_err(|err| DatabaseError::InitError(format!("{}", err))) 313 .unwrap(); 314 315 let database = LmdbDatabase::new(&ctx, &["a", "b"]) 316 .map_err(|err| DatabaseError::InitError(format!("{}", err))) 317 .unwrap(); 318 319 assert_database_count(0, &database); 320 assert_not_in_database(3, &database); 321 assert_not_in_database(5, &database); 322 323 // Add {3: 4} 324 let mut writer = database.writer().unwrap(); 325 writer.put(&[3], &[4]).unwrap(); 326 327 assert_database_count(0, &database); 328 assert_not_in_database(3, &database); 329 330 writer.commit().unwrap(); 331 332 assert_database_count(1, &database); 333 assert_key_value(3, 4, &database); 334 335 // Add {5: 6} 336 let mut writer = database.writer().unwrap(); 337 writer.put(&[5], &[6]).unwrap(); 338 writer.commit().unwrap(); 339 340 assert_database_count(2, &database); 341 assert_key_value(5, 6, &database); 342 assert_key_value(3, 4, &database); 343 344 // Delete {3: 4} 345 let mut writer = database.writer().unwrap(); 346 writer.delete(&[3]).unwrap(); 347 348 assert_database_count(2, &database); 349 350 writer.commit().unwrap(); 351 352 assert_database_count(1, &database); 353 assert_key_value(5, 6, &database); 354 assert_not_in_database(3, &database); 355 356 // Add {55: 5} in "a" 357 assert_index_count("a", 0, &database); 358 assert_index_count("b", 0, &database); 359 assert_not_in_index("a", 5, &database); 360 assert_not_in_index("b", 5, &database); 361 362 let mut writer = database.writer().unwrap(); 363 writer.index_put("a", &[55], &[5]).unwrap(); 364 365 assert_index_count("a", 0, &database); 366 assert_index_count("b", 0, &database); 367 assert_not_in_index("a", 5, &database); 368 assert_not_in_index("b", 5, &database); 369 370 writer.commit().unwrap(); 371 372 assert_index_count("a", 1, &database); 373 assert_index_count("b", 0, &database); 374 assert_index_key_value("a", 55, 5, &database); 375 assert_not_in_index("b", 5, &database); 376 assert_database_count(1, &database); 377 assert_key_value(5, 6, &database); 378 assert_not_in_database(3, &database); 379 380 // Delete {55: 5} in "a" 381 let mut writer = database.writer().unwrap(); 382 writer.index_delete("a", &[55]).unwrap(); 383 384 assert_index_count("a", 1, &database); 385 assert_index_count("b", 0, &database); 386 assert_index_key_value("a", 55, 5, &database); 387 assert_not_in_index("b", 5, &database); 388 389 writer.commit().unwrap(); 390 391 assert_index_count("a", 0, &database); 392 assert_index_count("b", 0, &database); 393 assert_not_in_index("a", 5, &database); 394 assert_not_in_index("b", 5, &database); 395 assert_database_count(1, &database); 396 assert_key_value(5, 6, &database); 397 assert_not_in_database(3, &database); 398 } 399 }