Skip to content

Commit 5e4c9e8

Browse files
committed
test: test
1 parent 8935695 commit 5e4c9e8

1 file changed

Lines changed: 75 additions & 27 deletions

File tree

store/src/lmdb.rs

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
//! Storage of core types using LMDB.
1616
1717
use heed::types::Bytes;
18-
use heed::{Database, Env, EnvOpenOptions, RoTxn, RwTxn, WithoutTls};
18+
use heed::{Database, Env, EnvOpenOptions, RoTxn, RwTxn, WithTls, WithoutTls};
19+
use lazy_static::lazy_static;
20+
use std::path::Path;
1921
use std::sync::atomic::{AtomicBool, Ordering};
2022
use std::sync::Arc;
2123
use std::time::Duration;
@@ -26,14 +28,19 @@ use crate::grin_core::ser::{self, DeserializationMode, ProtocolVersion};
2628
use crate::util::RwLock;
2729

2830
/// number of bytes to grow the database by when needed
29-
pub const ALLOC_CHUNK_SIZE_DEFAULT: usize = 134_217_728 / 32; //128 MB
31+
pub const ALLOC_CHUNK_SIZE_DEFAULT: usize = 134_217_728; //128 MB
3032
/// And for test mode, to avoid too much disk allocation on windows
3133
pub const ALLOC_CHUNK_SIZE_DEFAULT_TEST: usize = 1_048_576; //1 MB
3234
const RESIZE_PERCENT: f32 = 0.9;
3335
/// Want to ensure that each resize gives us at least this %
3436
/// of total space free
3537
const RESIZE_MIN_TARGET_PERCENT: f32 = 0.65;
3638

39+
lazy_static! {
40+
/// Check if environment is resizing.
41+
static ref RESIZING: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
42+
}
43+
3744
/// Main error type for this lmdb
3845
#[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)]
3946
pub enum Error {
@@ -80,51 +87,54 @@ where
8087

8188
const DEFAULT_DB_VERSION: ProtocolVersion = ProtocolVersion(3);
8289

90+
const ENV_NAME: &'static str = "lmdb";
91+
8392
/// LMDB-backed store facilitating data access and serialization. All writes
8493
/// are done through a Batch abstraction providing atomicity.
8594
pub struct Store {
86-
env: Arc<Env<WithoutTls>>,
8795
db: Arc<RwLock<Option<Arc<Database<Bytes, Bytes>>>>>,
96+
env: Arc<Env<WithTls>>,
8897
name: String,
8998
version: ProtocolVersion,
9099
alloc_chunk_size: usize,
91-
resizing: Arc<AtomicBool>,
92100
}
93101

94102
impl Store {
95103
/// Create a new LMDB env under the provided directory.
96104
/// By default creates an environment named "lmdb".
97105
/// Be aware of transactional semantics in lmdb
98106
/// (transactions are per environment, not per database).
107+
/// db with non-default `env_name` will be migrated into default environment.
99108
pub fn new(
100109
root_path: &str,
101110
env_name: Option<&str>,
102111
db_name: Option<&str>,
103112
max_readers: Option<u32>,
104113
) -> Result<Store, Error> {
105-
let name = match env_name {
106-
Some(n) => n.to_owned(),
107-
None => "lmdb".to_owned(),
108-
};
109-
let db_name = match db_name {
110-
Some(n) => n.to_owned(),
111-
None => "lmdb".to_owned(),
112-
};
113-
let full_path = [root_path.to_owned(), name].join("/");
114+
let name = env_name.unwrap_or_else(|| ENV_NAME);
115+
let db_name = db_name.unwrap_or_else(|| ENV_NAME);
116+
let mut full_path = Path::new(root_path).join(name);
117+
118+
// Fix path to use default environment.
119+
if name != ENV_NAME {
120+
full_path = Path::new(root_path).join(ENV_NAME);
121+
}
122+
114123
fs::create_dir_all(&full_path).map_err(|e| {
115124
Error::FileErr(format!(
116-
"Unable to create directory 'db_root' to store chain_data: {:?}",
117-
e
125+
"Unable to create {:?} to store data: {:?}",
126+
full_path, e
118127
))
119128
})?;
120129

121130
let alloc_chunk_size = match global::is_production_mode() {
122131
true => ALLOC_CHUNK_SIZE_DEFAULT,
123-
false => ALLOC_CHUNK_SIZE_DEFAULT_TEST,
132+
false => ALLOC_CHUNK_SIZE_DEFAULT,
124133
};
125134

135+
// Environment setup.
126136
let env = unsafe {
127-
let mut options = EnvOpenOptions::new().read_txn_without_tls();
137+
let mut options = EnvOpenOptions::new();
128138
let mut env_options = options.map_size(alloc_chunk_size).max_dbs(8);
129139
if let Some(max_readers) = max_readers {
130140
env_options = env_options.max_readers(max_readers);
@@ -137,13 +147,15 @@ impl Store {
137147
env.resize(new_size)?;
138148
};
139149
}
150+
debug!("DB Mapsize for {:?} is {}", full_path, env.info().map_size);
151+
152+
// Database setup.
140153
let s = Store {
141154
env: Arc::new(env),
142155
db: Arc::new(RwLock::new(None)),
143-
name: db_name,
156+
name: db_name.to_string(),
144157
version: DEFAULT_DB_VERSION,
145158
alloc_chunk_size,
146-
resizing: Default::default(),
147159
};
148160
{
149161
let mut write = s.env.write_txn()?;
@@ -152,9 +164,46 @@ impl Store {
152164
let mut w_db = s.db.write();
153165
*w_db = Some(Arc::new(db));
154166
}
167+
168+
// Migrate to default environment if needed.
169+
let migrate_from = Path::new(root_path).join(name);
170+
if name != ENV_NAME && migrate_from.exists() {
171+
debug!("Migrating {} to {:?}", name, full_path);
172+
if s.migrate_to_default_env(&migrate_from, db_name).is_ok() {
173+
let _ = fs::remove_dir_all(&migrate_from);
174+
} else {
175+
error!("Migrating {} failed", name);
176+
}
177+
}
155178
Ok(s)
179+
}
156180

157-
// debug!("DB Mapsize for {} is {}", full_path, env.info().map_size);
181+
/// Migrate db from provided path to store environment.
182+
fn migrate_to_default_env(&self, path: &Path, db_name: &str) -> Result<(), Error> {
183+
let env = unsafe {
184+
let mut options = EnvOpenOptions::new().read_txn_without_tls();
185+
let env_options = options.map_size(self.alloc_chunk_size).max_dbs(1);
186+
env_options.open(path)?
187+
};
188+
let db_from = {
189+
let mut write = env.write_txn()?;
190+
let db: Database<Bytes, Bytes> = env.create_database(&mut write, Some(db_name))?;
191+
write.commit()?;
192+
db
193+
};
194+
let db_to = self.db.read();
195+
let mut write_to = self.env.write_txn()?;
196+
let read_from = env.read_txn()?;
197+
let mut count = 0;
198+
for kv in db_from.iter(&read_from)? {
199+
count += 1;
200+
if let Ok((k, v)) = kv {
201+
db_to.as_ref().unwrap().put(&mut write_to, &k, &v)?;
202+
}
203+
}
204+
write_to.commit()?;
205+
debug!("Migrated {} records of {}", count, db_name);
206+
Ok(())
158207
}
159208

160209
/// Construct a new store using a specific protocol version.
@@ -166,7 +215,6 @@ impl Store {
166215
name: self.name.clone(),
167216
version,
168217
alloc_chunk_size: self.alloc_chunk_size,
169-
resizing: self.resizing.clone(),
170218
}
171219
}
172220

@@ -176,7 +224,7 @@ impl Store {
176224
}
177225

178226
/// Determines whether the environment needs a resize based on a simple percentage threshold.
179-
pub fn needs_resize(env: Arc<Env<WithoutTls>>, alloc_chunk_size: usize) -> (bool, usize) {
227+
pub fn needs_resize(env: Arc<Env<WithTls>>, alloc_chunk_size: usize) -> (bool, usize) {
180228
let env_info = env.info();
181229
let stat = env.stat();
182230
let size_used = stat.page_size as usize * env_info.last_page_number;
@@ -274,19 +322,19 @@ impl Store {
274322

275323
/// Builds a new batch to be used with this store.
276324
pub fn batch(&self) -> Result<Batch<'_>, Error> {
277-
while self.resizing.load(Ordering::SeqCst) {
325+
while RESIZING.load(Ordering::SeqCst) {
278326
debug!("Wait resizing {} DB", self.name);
279327
thread::sleep(Duration::from_millis(100));
280328
}
281329
let (resize, new_size) = Self::needs_resize(self.env.clone(), self.alloc_chunk_size);
282330
if resize {
283-
self.resizing.store(true, Ordering::SeqCst);
331+
RESIZING.store(true, Ordering::SeqCst);
284332
debug!("Start resizing {} DB", self.name);
285333
unsafe {
286334
thread::sleep(Duration::from_millis(3000));
287335
self.env.resize(new_size)?;
288336
}
289-
self.resizing.store(false, Ordering::SeqCst);
337+
RESIZING.store(false, Ordering::SeqCst);
290338
debug!("End resizing {} DB", self.name);
291339
}
292340
Ok(Batch::new(self)?)
@@ -419,7 +467,7 @@ where
419467
F: Fn(&[u8], &[u8]) -> Result<T, Error>,
420468
{
421469
db: Arc<Option<Arc<Database<Bytes, Bytes>>>>,
422-
read: Arc<RoTxn<'a, WithoutTls>>,
470+
read: Arc<RoTxn<'a, WithTls>>,
423471
skip: usize,
424472
prefix: Vec<u8>,
425473
deserialize: F,
@@ -464,7 +512,7 @@ where
464512
/// Initialize a new prefix iterator.
465513
pub fn new(
466514
db: Arc<Option<Arc<Database<Bytes, Bytes>>>>,
467-
read: RoTxn<'a, WithoutTls>,
515+
read: RoTxn<'a, WithTls>,
468516
prefix: &[u8],
469517
deserialize: F,
470518
) -> PrefixIterator<'a, F, T> {

0 commit comments

Comments
 (0)