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  }