Skip to content

Commit a89da7f

Browse files
committed
refactor: assorted cleanups of store
* use consistent spelling for `io::Reslut` * repplace `e.into()` with more explicit `io::Error::from` * implement From rather than Into * reduce minore code duplication by delegating to existing functions * use Arc::clone to make it explicit flow around store's identity * use buffered writer when dumping store to a file * fix latent bug around comparing wide pointers rust-lang/rust#69757. I thing we should not be hit by that due to full LTO, but that was a scary one. * remove some extra clones when working with cache
1 parent dede047 commit a89da7f

File tree

2 files changed

+65
-79
lines changed

2 files changed

+65
-79
lines changed

core/store/src/db.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ impl From<rocksdb::Error> for DBError {
3636
}
3737
}
3838

39-
impl Into<io::Error> for DBError {
40-
fn into(self) -> io::Error {
41-
io::Error::new(io::ErrorKind::Other, self)
39+
impl From<DBError> for io::Error {
40+
fn from(err: DBError) -> io::Error {
41+
io::Error::new(io::ErrorKind::Other, err)
4242
}
4343
}
4444

core/store/src/lib.rs

Lines changed: 62 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::fs::File;
2-
use std::io::{BufReader, Read, Write};
3-
use std::ops::Deref;
2+
use std::io::{BufReader, BufWriter, Read, Write};
43
use std::path::Path;
54
use std::sync::Arc;
65
use std::{fmt, io};
@@ -53,31 +52,26 @@ impl Store {
5352
Store { storage }
5453
}
5554

56-
pub fn get(&self, column: DBCol, key: &[u8]) -> Result<Option<Vec<u8>>, io::Error> {
57-
self.storage.get(column, key).map_err(|e| e.into())
55+
pub fn get(&self, column: DBCol, key: &[u8]) -> io::Result<Option<Vec<u8>>> {
56+
self.storage.get(column, key).map_err(io::Error::from)
5857
}
5958

60-
pub fn get_ser<T: BorshDeserialize>(
61-
&self,
62-
column: DBCol,
63-
key: &[u8],
64-
) -> Result<Option<T>, io::Error> {
65-
match self.storage.get(column, key) {
66-
Ok(Some(bytes)) => match T::try_from_slice(bytes.as_ref()) {
59+
pub fn get_ser<T: BorshDeserialize>(&self, column: DBCol, key: &[u8]) -> io::Result<Option<T>> {
60+
match self.get(column, key)? {
61+
Some(bytes) => match T::try_from_slice(&bytes) {
6762
Ok(result) => Ok(Some(result)),
6863
Err(e) => Err(e),
6964
},
70-
Ok(None) => Ok(None),
71-
Err(e) => Err(e.into()),
65+
None => Ok(None),
7266
}
7367
}
7468

75-
pub fn exists(&self, column: DBCol, key: &[u8]) -> Result<bool, io::Error> {
76-
self.storage.get(column, key).map(|value| value.is_some()).map_err(|e| e.into())
69+
pub fn exists(&self, column: DBCol, key: &[u8]) -> io::Result<bool> {
70+
self.get(column, key).map(|value| value.is_some())
7771
}
7872

7973
pub fn store_update(&self) -> StoreUpdate {
80-
StoreUpdate::new(self.storage.clone())
74+
StoreUpdate::new(Arc::clone(&self.storage))
8175
}
8276

8377
pub fn iter<'a>(
@@ -106,16 +100,15 @@ impl Store {
106100
&'a self,
107101
column: DBCol,
108102
key_prefix: &'a [u8],
109-
) -> Box<dyn Iterator<Item = Result<(Vec<u8>, T), io::Error>> + 'a> {
110-
Box::new(
111-
self.storage
112-
.iter_prefix(column, key_prefix)
113-
.map(|(key, value)| Ok((key.to_vec(), T::try_from_slice(value.as_ref())?))),
114-
)
103+
) -> impl Iterator<Item = io::Result<(Box<[u8]>, T)>> + 'a {
104+
self.storage
105+
.iter_prefix(column, key_prefix)
106+
.map(|(key, value)| Ok((key, T::try_from_slice(value.as_ref())?)))
115107
}
116108

117-
pub fn save_to_file(&self, column: DBCol, filename: &Path) -> Result<(), std::io::Error> {
118-
let mut file = File::create(filename)?;
109+
pub fn save_to_file(&self, column: DBCol, filename: &Path) -> io::Result<()> {
110+
let file = File::create(filename)?;
111+
let mut file = BufWriter::new(file);
119112
for (key, value) in self.storage.iter_without_rc_logic(column) {
120113
file.write_u32::<LittleEndian>(key.len() as u32)?;
121114
file.write_all(&key)?;
@@ -125,7 +118,7 @@ impl Store {
125118
Ok(())
126119
}
127120

128-
pub fn load_from_file(&self, column: DBCol, filename: &Path) -> Result<(), std::io::Error> {
121+
pub fn load_from_file(&self, column: DBCol, filename: &Path) -> io::Result<()> {
129122
let file = File::open(filename)?;
130123
let mut file = BufReader::new(file);
131124
let mut transaction = self.storage.transaction();
@@ -134,7 +127,7 @@ impl Store {
134127
loop {
135128
let key_len = match file.read_u32::<LittleEndian>() {
136129
Ok(key_len) => key_len as usize,
137-
Err(ref err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break,
130+
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => break,
138131
Err(err) => return Err(err),
139132
};
140133
key.resize(key_len, 0);
@@ -146,7 +139,7 @@ impl Store {
146139

147140
transaction.put(column, &key, &value);
148141
}
149-
self.storage.write(transaction).map_err(|e| e.into())
142+
self.storage.write(transaction).map_err(io::Error::from)
150143
}
151144

152145
pub fn get_rocksdb(&self) -> Option<&RocksDB> {
@@ -173,7 +166,7 @@ impl StoreUpdate {
173166
}
174167

175168
pub fn new_with_tries(tries: ShardTries) -> Self {
176-
let storage = tries.get_store().storage.clone();
169+
let storage = Arc::clone(&tries.get_store().storage);
177170
let transaction = storage.transaction();
178171
StoreUpdate { storage, transaction, tries: Some(tries) }
179172
}
@@ -193,7 +186,7 @@ impl StoreUpdate {
193186
column: DBCol,
194187
key: &[u8],
195188
value: &T,
196-
) -> Result<(), io::Error> {
189+
) -> io::Result<()> {
197190
debug_assert!(!column.is_rc());
198191
let data = value.try_to_vec()?;
199192
self.set(column, key, &data);
@@ -210,19 +203,17 @@ impl StoreUpdate {
210203

211204
/// Merge another store update into this one.
212205
pub fn merge(&mut self, other: StoreUpdate) {
213-
if let Some(tries) = other.tries {
214-
if self.tries.is_none() {
215-
self.tries = Some(tries);
216-
} else {
217-
debug_assert!(self.tries.as_ref().unwrap().is_same(&tries));
218-
}
206+
match (&self.tries, other.tries) {
207+
(None | Some(_), None) => (),
208+
(None, Some(tries)) => self.tries = Some(tries),
209+
(Some(t1), Some(t2)) => debug_assert!(t1.is_same(&t2)),
219210
}
220211

221212
self.merge_transaction(other.transaction);
222213
}
223214

224215
/// Merge DB Transaction.
225-
pub fn merge_transaction(&mut self, transaction: DBTransaction) {
216+
fn merge_transaction(&mut self, transaction: DBTransaction) {
226217
for op in transaction.ops {
227218
match op {
228219
DBOp::Insert { col, key, value } => self.transaction.put(col, &key, &value),
@@ -235,18 +226,18 @@ impl StoreUpdate {
235226
}
236227
}
237228

238-
pub fn commit(self) -> Result<(), io::Error> {
229+
pub fn commit(self) -> io::Result<()> {
239230
debug_assert!(
240231
{
241232
let non_refcount_keys = self
242233
.transaction
243234
.ops
244235
.iter()
245236
.filter_map(|op| match op {
246-
DBOp::Insert { col, key, .. } => Some((*col as u8, key)),
247-
DBOp::Delete { col, key } => Some((*col as u8, key)),
248-
DBOp::UpdateRefcount { .. } => None,
249-
DBOp::DeleteAll { .. } => None,
237+
DBOp::Insert { col, key, .. } | DBOp::Delete { col, key } => {
238+
Some((*col as u8, key))
239+
}
240+
DBOp::UpdateRefcount { .. } | DBOp::DeleteAll { .. } => None,
250241
})
251242
.collect::<Vec<_>>();
252243
non_refcount_keys.len()
@@ -256,13 +247,13 @@ impl StoreUpdate {
256247
self
257248
);
258249
if let Some(tries) = self.tries {
259-
assert_eq!(
260-
tries.get_store().storage.deref() as *const _,
261-
self.storage.deref() as *const _
262-
);
250+
// Note: avoid comparing wide pointers here to work-around
251+
// https://github.com/rust-lang/rust/issues/69757
252+
let addr = |arc| Arc::as_ptr(arc) as *const u8;
253+
assert_eq!(addr(&tries.get_store().storage), addr(&self.storage),);
263254
tries.update_cache(&self.transaction)?;
264255
}
265-
self.storage.write(self.transaction).map_err(|e| e.into())
256+
self.storage.write(self.transaction).map_err(io::Error::from)
266257
}
267258
}
268259

@@ -289,22 +280,18 @@ pub fn read_with_cache<'a, T: BorshDeserialize + 'a>(
289280
cache: &'a mut LruCache<Vec<u8>, T>,
290281
key: &[u8],
291282
) -> io::Result<Option<&'a T>> {
292-
let key_vec = key.to_vec();
293-
if cache.get(&key_vec).is_some() {
294-
return Ok(Some(cache.get(&key_vec).unwrap()));
283+
// Note: Due to `&mut -> &` conversions, it's not possible to avoid double
284+
// hash map lookups here.
285+
if cache.contains(key) {
286+
return Ok(cache.get(key));
295287
}
296288
if let Some(result) = storage.get_ser(col, key)? {
297289
cache.put(key.to_vec(), result);
298-
return Ok(cache.get(&key_vec));
290+
return Ok(cache.get(key));
299291
}
300292
Ok(None)
301293
}
302294

303-
pub fn create_store(path: &Path) -> Store {
304-
let db = Arc::new(RocksDB::new(path).expect("Failed to open the database"));
305-
Store::new(db)
306-
}
307-
308295
#[derive(Default, Debug)]
309296
pub struct StoreConfig {
310297
/// Attempted writes to the DB will fail. Doesn't require a `LOCK` file.
@@ -314,17 +301,19 @@ pub struct StoreConfig {
314301
pub enable_statistics: bool,
315302
}
316303

304+
pub fn create_store(path: &Path) -> Store {
305+
create_store_with_config(path, StoreConfig::default())
306+
}
307+
317308
pub fn create_store_with_config(path: &Path, store_config: StoreConfig) -> Store {
318309
let mut opts = RocksDBOptions::default();
319310
if store_config.enable_statistics {
320311
opts = opts.enable_statistics();
321312
}
322313

323-
let db = Arc::new(
324-
(if store_config.read_only { opts.read_only(path) } else { opts.read_write(path) })
325-
.expect("Failed to open the database"),
326-
);
327-
Store::new(db)
314+
let db = if store_config.read_only { opts.read_only(path) } else { opts.read_write(path) }
315+
.expect("Failed to open the database");
316+
Store::new(Arc::new(db))
328317
}
329318

330319
/// Reads an object from Trie.
@@ -334,18 +323,15 @@ pub fn get<T: BorshDeserialize>(
334323
state_update: &TrieUpdate,
335324
key: &TrieKey,
336325
) -> Result<Option<T>, StorageError> {
337-
state_update.get(key).and_then(|opt| {
338-
opt.map_or_else(
339-
|| Ok(None),
340-
|data| {
341-
T::try_from_slice(&data)
342-
.map_err(|_| {
343-
StorageError::StorageInconsistentState("Failed to deserialize".to_string())
344-
})
345-
.map(Some)
346-
},
347-
)
348-
})
326+
match state_update.get(key)? {
327+
None => Ok(None),
328+
Some(data) => match T::try_from_slice(&data) {
329+
Err(_err) => {
330+
Err(StorageError::StorageInconsistentState("Failed to deserialize".to_string()))
331+
}
332+
Ok(value) => Ok(Some(value)),
333+
},
334+
}
349335
}
350336

351337
/// Writes an object into Trie.
@@ -509,11 +495,11 @@ pub fn remove_account(
509495
Ok(())
510496
}
511497

512-
pub fn get_genesis_state_roots(store: &Store) -> Result<Option<Vec<StateRoot>>, std::io::Error> {
498+
pub fn get_genesis_state_roots(store: &Store) -> io::Result<Option<Vec<StateRoot>>> {
513499
store.get_ser::<Vec<StateRoot>>(DBCol::ColBlockMisc, GENESIS_STATE_ROOTS_KEY)
514500
}
515501

516-
pub fn get_genesis_hash(store: &Store) -> Result<Option<CryptoHash>, std::io::Error> {
502+
pub fn get_genesis_hash(store: &Store) -> io::Result<Option<CryptoHash>> {
517503
store.get_ser::<CryptoHash>(DBCol::ColBlockMisc, GENESIS_JSON_HASH_KEY)
518504
}
519505

@@ -538,13 +524,13 @@ pub struct StoreCompiledContractCache {
538524
/// Key must take into account VM being used and its configuration, so that
539525
/// we don't cache non-gas metered binaries, for example.
540526
impl CompiledContractCache for StoreCompiledContractCache {
541-
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), std::io::Error> {
527+
fn put(&self, key: &[u8], value: &[u8]) -> io::Result<()> {
542528
let mut store_update = self.store.store_update();
543529
store_update.set(DBCol::ColCachedContractCode, key, value);
544530
store_update.commit()
545531
}
546532

547-
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, std::io::Error> {
533+
fn get(&self, key: &[u8]) -> io::Result<Option<Vec<u8>>> {
548534
self.store.get(DBCol::ColCachedContractCode, key)
549535
}
550536
}

0 commit comments

Comments
 (0)