1515//! Storage of core types using LMDB.
1616
1717use 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 ;
1921use std:: sync:: atomic:: { AtomicBool , Ordering } ;
2022use std:: sync:: Arc ;
2123use std:: time:: Duration ;
@@ -26,14 +28,19 @@ use crate::grin_core::ser::{self, DeserializationMode, ProtocolVersion};
2628use 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
3133pub const ALLOC_CHUNK_SIZE_DEFAULT_TEST : usize = 1_048_576 ; //1 MB
3234const RESIZE_PERCENT : f32 = 0.9 ;
3335/// Want to ensure that each resize gives us at least this %
3436/// of total space free
3537const 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 ) ]
3946pub enum Error {
@@ -80,51 +87,54 @@ where
8087
8188const 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.
8594pub 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
94102impl 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