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  }