mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1557161 - enable rkv consumers to migrate LMDB environments across architecture changes r=lina,keeler,froydnj
Differential Revision: https://phabricator.services.mozilla.com/D33863 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
0feee6b2a0
commit
9edec819af
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -444,11 +444,12 @@ dependencies = [
|
||||
"moz_task 0.1.0",
|
||||
"nserror 0.1.0",
|
||||
"nsstring 0.1.0",
|
||||
"rkv 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rkv 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust_cascade 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"storage_variant 0.1.0",
|
||||
"style 0.0.1",
|
||||
"tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xpcom 0.1.0",
|
||||
@ -1499,8 +1500,9 @@ dependencies = [
|
||||
"moz_task 0.1.0",
|
||||
"nserror 0.1.0",
|
||||
"nsstring 0.1.0",
|
||||
"rkv 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rkv 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"storage_variant 0.1.0",
|
||||
"tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xpcom 0.1.0",
|
||||
]
|
||||
@ -2439,11 +2441,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rkv"
|
||||
version = "0.9.5"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lmdb-rkv 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3571,8 +3575,9 @@ dependencies = [
|
||||
"moz_task 0.1.0",
|
||||
"nserror 0.1.0",
|
||||
"nsstring 0.1.0",
|
||||
"rkv 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rkv 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xpcom 0.1.0",
|
||||
]
|
||||
|
||||
@ -3816,7 +3821,7 @@ dependencies = [
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b"
|
||||
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
|
||||
"checksum rkv 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c1b8d667bf149bfac7c47bb728dfb7246f35fdf61c2f16f9f588194f087d23c"
|
||||
"checksum rkv 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9ebeb7e046283b72b4bcd3b8ee4720cf69cd09f5b140b5ab46495df4af0e5113"
|
||||
"checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399"
|
||||
"checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
|
||||
"checksum rust-ini 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a654c5bda722c699be6b0fe4c0d90de218928da5b724c3e467fc48865c37263"
|
||||
|
@ -12,11 +12,12 @@ log = "0.4"
|
||||
moz_task = { path = "../../../../xpcom/rust/moz_task" }
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
rkv = "^0.9"
|
||||
rkv = "^0.9.6"
|
||||
rust_cascade = "0.3.4"
|
||||
sha2 = "^0.8"
|
||||
storage_variant = { path = "../../../../storage/variant" }
|
||||
style = { path = "../../../../servo/components/style" }
|
||||
tempfile = "3"
|
||||
thin-vec = { version = "0.1.0", features = ["gecko-ffi"] }
|
||||
time = "0.1"
|
||||
xpcom = { path = "../../../../xpcom/rust/xpcom" }
|
||||
|
@ -19,10 +19,11 @@ extern crate time;
|
||||
extern crate xpcom;
|
||||
extern crate storage_variant;
|
||||
extern crate style;
|
||||
extern crate tempfile;
|
||||
|
||||
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use lmdb::EnvironmentFlags;
|
||||
use lmdb::{EnvironmentFlags, Error as LmdbError};
|
||||
use moz_task::{create_thread, is_main_thread, Task, TaskRunnable};
|
||||
use nserror::{
|
||||
nsresult, NS_ERROR_FAILURE, NS_ERROR_NOT_SAME_THREAD, NS_ERROR_NO_AGGREGATION,
|
||||
@ -30,12 +31,12 @@ use nserror::{
|
||||
};
|
||||
use nsstring::{nsACString, nsAString, nsCStr, nsCString, nsString};
|
||||
use rkv::error::StoreError;
|
||||
use rkv::{Rkv, SingleStore, StoreOptions, Value};
|
||||
use rkv::{migrate::Migrator, Rkv, SingleStore, StoreOptions, Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::Display;
|
||||
use std::fs::{create_dir_all, remove_file, File};
|
||||
use std::fs::{copy, create_dir_all, remove_file, File};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::mem::size_of;
|
||||
use std::os::raw::c_char;
|
||||
@ -45,6 +46,7 @@ use std::str;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::time::{Duration, SystemTime};
|
||||
use storage_variant::VariantType;
|
||||
use tempfile::tempdir;
|
||||
use thin_vec::ThinVec;
|
||||
use xpcom::interfaces::{
|
||||
nsICRLiteState, nsICertInfo, nsICertStorage, nsICertStorageCallback, nsIFile,
|
||||
@ -788,7 +790,18 @@ fn make_env(path: &Path) -> Result<Rkv, SecurityStateError> {
|
||||
let mut builder = Rkv::environment_builder();
|
||||
builder.set_max_dbs(2);
|
||||
builder.set_map_size(16777216); // 16MB
|
||||
Rkv::from_env(path, builder).map_err(SecurityStateError::from)
|
||||
match Rkv::from_env(path, builder) {
|
||||
Ok(env) => Ok(env),
|
||||
Err(StoreError::LmdbError(LmdbError::Invalid)) => {
|
||||
let temp_env = tempdir()?;
|
||||
let mut migrator = Migrator::new(&path)?;
|
||||
migrator.migrate(temp_env.path())?;
|
||||
copy(temp_env.path().join("data.mdb"), path.join("data.mdb"))?;
|
||||
copy(temp_env.path().join("lock.mdb"), path.join("lock.mdb"))?;
|
||||
Rkv::from_env(path, builder)
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
}.map_err(SecurityStateError::from)
|
||||
}
|
||||
|
||||
fn unconditionally_remove_file(path: &Path) -> Result<(), SecurityStateError> {
|
||||
|
2
third_party/rust/rkv/.cargo-checksum.json
vendored
2
third_party/rust/rkv/.cargo-checksum.json
vendored
@ -1 +1 @@
|
||||
{"files":{"CODE_OF_CONDUCT.md":"902d5357af363426631d907e641e220b3ec89039164743f8442b3f120479b7cf","Cargo.toml":"27ac84d5a81e9b054e79f7615086b5625071aa88803202f7c8de4a1e53e32e83","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"9dc24375b49fef42f35dec42e316e21827d7337622f9e7cf36243cd28808797a","examples/README.md":"143767fc145bf167ce269a65138cb3f7086cb715b8bc4f73626da82966e646f4","examples/iterator.rs":"ddc3997e394a30ad82d78d2675a48c4617353f88b89bb9a3df5a3804d59b8ef9","examples/simple-store.rs":"cae63e39f2f98ee6ac2f387dcb02d6b929828a74f32f7d18d69c7fc9c3cce765","run-all-examples.sh":"7f9d11d01017f77e1c9d26e3e82dfca8c6930deaec85e864458e33a7fa267de0","src/env.rs":"67b4a98d9631bb61ca6181afc557b7fda0876de2959cfb78bd7049da3cd0a623","src/error.rs":"46632b8fcb1070a1860247e09a59d39772079ebfba5d3d1bbee03d08e1252275","src/lib.rs":"6b21c99c067f550124986b3c15d018b1a090351c339758c0a76ba4f58f818f5d","src/manager.rs":"6142e31ec0d31fa18ed0fe29b123de020ad81ecaec21263c374742548f842ee8","src/readwrite.rs":"fde695333e4845f4f53d63da6281f585919e2a3ac5cfe00d173cc139bc822763","src/store.rs":"409d13b1ea0d1254dae947ecbce50e741fb71c3ca118a78803b734336dce6a8f","src/store/integer.rs":"f386474c971f671c9b316a16ebff5b586be6837c886f443753ae13277a7e0070","src/store/integermulti.rs":"1a0912f97619297da31cc8c146e38941b88539d2857df81191a49c8dbd18625d","src/store/multi.rs":"2dec01c2202a2c9069cced4e1e42906b01d0b85df25d17e0ea810c05fa8395d0","src/store/single.rs":"c55c3600714f5ed9e820b16c2335ae00a0071174e0a32b9df89a34182a4b908c","src/value.rs":"7fae77a8291b951591e557ec694bfdadc9eb78557dad36a970cfcdcfb83fd238","tests/integer-store.rs":"f7e06c71b0dead2323c7c61fc8bcbffbdd3a4796eebf6138db9cce3dbba716a3","tests/manager.rs":"97ec61145dc227f4f5fbcb6449c096bbe5b9a09db4e61ff4491c0443fe9adf26","tests/multi-integer-store.rs":"83295b0135c502321304aa06b05d5a9eeab41b1438ed7ddf2cb1a3613dfef4d9"},"package":"2c1b8d667bf149bfac7c47bb728dfb7246f35fdf61c2f16f9f588194f087d23c"}
|
||||
{"files":{"CODE_OF_CONDUCT.md":"902d5357af363426631d907e641e220b3ec89039164743f8442b3f120479b7cf","Cargo.toml":"24142309339fd4e5a3d8be99a31240a56da1475aa9aaa79fcae05402f1ad66ba","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"e28eb7d26ddd6dd71e1757f4eab63044b5c430932ef3c3a24e9772ddc78ebf85","examples/README.md":"143767fc145bf167ce269a65138cb3f7086cb715b8bc4f73626da82966e646f4","examples/iterator.rs":"ddc3997e394a30ad82d78d2675a48c4617353f88b89bb9a3df5a3804d59b8ef9","examples/simple-store.rs":"cae63e39f2f98ee6ac2f387dcb02d6b929828a74f32f7d18d69c7fc9c3cce765","run-all-examples.sh":"7f9d11d01017f77e1c9d26e3e82dfca8c6930deaec85e864458e33a7fa267de0","src/bin/dump.rs":"da8543848e57893902751f4c4745e835b9c86263da2344af18d5717014f645f5","src/bin/rand.rs":"3da924fa0f1a118f606e2b94aee3a0553d9ebdbd17ee0152b85148adbf521bba","src/env.rs":"67b4a98d9631bb61ca6181afc557b7fda0876de2959cfb78bd7049da3cd0a623","src/error.rs":"f2cbab99691f36c98c24d297de3a303de258ddd3a06e2f54cb5efce20eb3740b","src/lib.rs":"11c2962555c14d9c7b426cf2a50f5fa04600ad6fc8805e6486f69e7ad1ed1cf2","src/manager.rs":"f4eb7f0845e036ff212cdb82e70b19920a2ed4fbcfe4f31ddf33368ea4383d05","src/migrate.rs":"674cee0d027fc2eed3b09cebe686c837a97725099c967d8c2f49d19e793e6bfd","src/readwrite.rs":"fde695333e4845f4f53d63da6281f585919e2a3ac5cfe00d173cc139bc822763","src/store.rs":"409d13b1ea0d1254dae947ecbce50e741fb71c3ca118a78803b734336dce6a8f","src/store/integer.rs":"f386474c971f671c9b316a16ebff5b586be6837c886f443753ae13277a7e0070","src/store/integermulti.rs":"1a0912f97619297da31cc8c146e38941b88539d2857df81191a49c8dbd18625d","src/store/multi.rs":"2dec01c2202a2c9069cced4e1e42906b01d0b85df25d17e0ea810c05fa8395d0","src/store/single.rs":"c55c3600714f5ed9e820b16c2335ae00a0071174e0a32b9df89a34182a4b908c","src/value.rs":"7fae77a8291b951591e557ec694bfdadc9eb78557dad36a970cfcdcfb83fd238","tests/integer-store.rs":"f7e06c71b0dead2323c7c61fc8bcbffbdd3a4796eebf6138db9cce3dbba716a3","tests/manager.rs":"97ec61145dc227f4f5fbcb6449c096bbe5b9a09db4e61ff4491c0443fe9adf26","tests/multi-integer-store.rs":"83295b0135c502321304aa06b05d5a9eeab41b1438ed7ddf2cb1a3613dfef4d9"},"package":"9ebeb7e046283b72b4bcd3b8ee4720cf69cd09f5b140b5ab46495df4af0e5113"}
|
9
third_party/rust/rkv/Cargo.toml
vendored
9
third_party/rust/rkv/Cargo.toml
vendored
@ -13,8 +13,9 @@
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "rkv"
|
||||
version = "0.9.5"
|
||||
version = "0.9.6"
|
||||
authors = ["Richard Newman <rnewman@twinql.com>", "Nan Jiang <najiang@mozilla.com>", "Myk Melez <myk@mykzilla.org>"]
|
||||
exclude = ["/tests/envs/*"]
|
||||
description = "a simple, humane, typed Rust interface to LMDB"
|
||||
homepage = "https://github.com/mozilla/rkv"
|
||||
readme = "README.md"
|
||||
@ -28,6 +29,12 @@ version = "0.3"
|
||||
[dependencies.bincode]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "1"
|
||||
|
||||
[dependencies.byteorder]
|
||||
version = "1"
|
||||
|
||||
[dependencies.failure]
|
||||
version = "0.1.2"
|
||||
features = ["derive"]
|
||||
|
2
third_party/rust/rkv/README.md
vendored
2
third_party/rust/rkv/README.md
vendored
@ -40,6 +40,8 @@ The project includes unit and doc tests embedded in the `src/` files, integratio
|
||||
./run-all-examples.sh
|
||||
```
|
||||
|
||||
Note: the test fixtures in the `tests/envs/` subdirectory aren't included in the package published to crates.io, so you must clone this repository in order to run the tests that depend on those fixtures or use the `rand` and `dump` executables to recreate them.
|
||||
|
||||
## Contribute
|
||||
|
||||
Of the various open source archetypes described in [A Framework for Purposeful Open Source](https://medium.com/mozilla-open-innovation/whats-your-open-source-strategy-here-are-10-answers-383221b3f9d3), the rkv project most closely resembles the Specialty Library, and we welcome contributions. Please report problems or ask questions using this repo's GitHub [issue tracker](https://github.com/mozilla/rkv/issues) and submit [pull requests](https://github.com/mozilla/rkv/pulls) for code and documentation changes.
|
||||
|
55
third_party/rust/rkv/src/bin/dump.rs
vendored
Normal file
55
third_party/rust/rkv/src/bin/dump.rs
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
extern crate rkv;
|
||||
|
||||
use rkv::{
|
||||
error::MigrateError,
|
||||
migrate::Migrator,
|
||||
};
|
||||
use std::{
|
||||
env::args,
|
||||
io,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
fn main() -> Result<(), MigrateError> {
|
||||
let mut cli_args = args();
|
||||
let mut db_name = None;
|
||||
let mut env_path = None;
|
||||
|
||||
// The first arg is the name of the program, which we can ignore.
|
||||
cli_args.next();
|
||||
|
||||
while let Some(arg) = cli_args.next() {
|
||||
if &arg[0..1] == "-" {
|
||||
match &arg[1..] {
|
||||
"s" => {
|
||||
db_name = match cli_args.next() {
|
||||
None => return Err("-s must be followed by database name".into()),
|
||||
Some(str) => Some(str),
|
||||
};
|
||||
},
|
||||
str => return Err(format!("arg -{} not recognized", str).into()),
|
||||
}
|
||||
} else {
|
||||
if env_path.is_some() {
|
||||
return Err("must provide only one path to the LMDB environment".into());
|
||||
}
|
||||
env_path = Some(arg);
|
||||
}
|
||||
}
|
||||
|
||||
let env_path = env_path.ok_or("must provide a path to the LMDB environment")?;
|
||||
let mut migrator: Migrator = Migrator::new(Path::new(&env_path))?;
|
||||
migrator.dump(db_name.as_ref().map(String::as_str), io::stdout()).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
114
third_party/rust/rkv/src/bin/rand.rs
vendored
Normal file
114
third_party/rust/rkv/src/bin/rand.rs
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
//! A command-line utility to create an LMDB environment containing random data.
|
||||
//! It requires one flag, `-s path/to/environment`, which specifies the location
|
||||
//! where the tool should create the environment. Optionally, you may specify
|
||||
//! the number of key/value pairs to create via the `-n <number>` flag
|
||||
//! (for which the default value is 50).
|
||||
|
||||
extern crate rkv;
|
||||
|
||||
use rkv::{
|
||||
Rkv,
|
||||
SingleStore,
|
||||
StoreOptions,
|
||||
Value,
|
||||
};
|
||||
use std::{
|
||||
env::args,
|
||||
fs::{
|
||||
create_dir_all,
|
||||
File,
|
||||
},
|
||||
io::Read,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut args = args();
|
||||
let mut database = None;
|
||||
let mut path = None;
|
||||
let mut num_pairs = 50;
|
||||
|
||||
// The first arg is the name of the program, which we can ignore.
|
||||
args.next();
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
if &arg[0..1] == "-" {
|
||||
match &arg[1..] {
|
||||
"s" => {
|
||||
database = match args.next() {
|
||||
None => panic!("-s must be followed by database arg"),
|
||||
Some(str) => Some(str),
|
||||
};
|
||||
},
|
||||
"n" => {
|
||||
num_pairs = match args.next() {
|
||||
None => panic!("-s must be followed by number of pairs"),
|
||||
Some(str) => str.parse().expect("number"),
|
||||
};
|
||||
},
|
||||
str => panic!("arg -{} not recognized", str),
|
||||
}
|
||||
} else {
|
||||
if path.is_some() {
|
||||
panic!("must provide only one path to the LMDB environment");
|
||||
}
|
||||
path = Some(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if path.is_none() {
|
||||
panic!("must provide a path to the LMDB environment");
|
||||
}
|
||||
let path = path.unwrap();
|
||||
|
||||
create_dir_all(&path).expect("dir created");
|
||||
|
||||
let mut builder = Rkv::environment_builder();
|
||||
builder.set_max_dbs(2);
|
||||
// Allocate enough map to accommodate the largest random collection.
|
||||
// We currently do this by allocating twice the maximum possible size
|
||||
// of the pairs (assuming maximum key and value sizes).
|
||||
builder.set_map_size((511 + 65535) * num_pairs * 2);
|
||||
let rkv = Rkv::from_env(Path::new(&path), builder).expect("Rkv");
|
||||
let store: SingleStore =
|
||||
rkv.open_single(database.as_ref().map(|x| x.as_str()), StoreOptions::create()).expect("opened");
|
||||
let mut writer = rkv.write().expect("writer");
|
||||
|
||||
// Generate random values for the number of keys and key/value lengths.
|
||||
// On Linux, "Just use /dev/urandom!" <https://www.2uo.de/myths-about-urandom/>.
|
||||
// On macOS it doesn't matter (/dev/random and /dev/urandom are identical).
|
||||
let mut random = File::open("/dev/urandom").unwrap();
|
||||
let mut nums = [0u8; 4];
|
||||
random.read_exact(&mut nums).unwrap();
|
||||
|
||||
// Generate 0–255 pairs.
|
||||
for _ in 0..num_pairs {
|
||||
// Generate key and value lengths. The key must be 1–511 bytes long.
|
||||
// The value length can be 0 and is essentially unbounded; we generate
|
||||
// value lengths of 0–0xffff (65535).
|
||||
// NB: the modulus method for generating a random number within a range
|
||||
// introduces distribution skew, but we don't need it to be perfect.
|
||||
let key_len = ((u16::from(nums[0]) + (u16::from(nums[1]) << 8)) % 511 + 1) as usize;
|
||||
let value_len = (u16::from(nums[2]) + (u16::from(nums[3]) << 8)) as usize;
|
||||
|
||||
let mut key: Vec<u8> = vec![0; key_len];
|
||||
random.read_exact(&mut key[0..key_len]).unwrap();
|
||||
|
||||
let mut value: Vec<u8> = vec![0; value_len];
|
||||
random.read_exact(&mut value[0..value_len]).unwrap();
|
||||
|
||||
store.put(&mut writer, key, &Value::Blob(&value)).expect("wrote");
|
||||
}
|
||||
|
||||
writer.commit().expect("committed");
|
||||
}
|
92
third_party/rust/rkv/src/error.rs
vendored
92
third_party/rust/rkv/src/error.rs
vendored
@ -96,3 +96,95 @@ impl From<::std::io::Error> for StoreError {
|
||||
StoreError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum MigrateError {
|
||||
#[fail(display = "database not found: {:?}", _0)]
|
||||
DatabaseNotFound(String),
|
||||
|
||||
#[fail(display = "{}", _0)]
|
||||
FromString(String),
|
||||
|
||||
#[fail(display = "couldn't determine bit depth")]
|
||||
IndeterminateBitDepth,
|
||||
|
||||
#[fail(display = "I/O error: {:?}", _0)]
|
||||
IoError(::std::io::Error),
|
||||
|
||||
#[fail(display = "invalid DatabaseFlags bits")]
|
||||
InvalidDatabaseBits,
|
||||
|
||||
#[fail(display = "invalid data version")]
|
||||
InvalidDataVersion,
|
||||
|
||||
#[fail(display = "invalid magic number")]
|
||||
InvalidMagicNum,
|
||||
|
||||
#[fail(display = "invalid NodeFlags bits")]
|
||||
InvalidNodeBits,
|
||||
|
||||
#[fail(display = "invalid PageFlags bits")]
|
||||
InvalidPageBits,
|
||||
|
||||
#[fail(display = "invalid page number")]
|
||||
InvalidPageNum,
|
||||
|
||||
#[fail(display = "lmdb error: {}", _0)]
|
||||
LmdbError(lmdb::Error),
|
||||
|
||||
#[fail(display = "string conversion error")]
|
||||
StringConversionError,
|
||||
|
||||
#[fail(display = "TryFromInt error: {:?}", _0)]
|
||||
TryFromIntError(::std::num::TryFromIntError),
|
||||
|
||||
#[fail(display = "unexpected Page variant")]
|
||||
UnexpectedPageVariant,
|
||||
|
||||
#[fail(display = "unexpected PageHeader variant")]
|
||||
UnexpectedPageHeaderVariant,
|
||||
|
||||
#[fail(display = "unsupported PageHeader variant")]
|
||||
UnsupportedPageHeaderVariant,
|
||||
|
||||
#[fail(display = "UTF8 error: {:?}", _0)]
|
||||
Utf8Error(::std::str::Utf8Error),
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for MigrateError {
|
||||
fn from(e: ::std::io::Error) -> MigrateError {
|
||||
MigrateError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::str::Utf8Error> for MigrateError {
|
||||
fn from(e: ::std::str::Utf8Error) -> MigrateError {
|
||||
MigrateError::Utf8Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::num::TryFromIntError> for MigrateError {
|
||||
fn from(e: ::std::num::TryFromIntError) -> MigrateError {
|
||||
MigrateError::TryFromIntError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MigrateError {
|
||||
fn from(e: &str) -> MigrateError {
|
||||
MigrateError::FromString(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MigrateError {
|
||||
fn from(e: String) -> MigrateError {
|
||||
MigrateError::FromString(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lmdb::Error> for MigrateError {
|
||||
fn from(e: lmdb::Error) -> MigrateError {
|
||||
match e {
|
||||
e => MigrateError::LmdbError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1
third_party/rust/rkv/src/lib.rs
vendored
1
third_party/rust/rkv/src/lib.rs
vendored
@ -209,6 +209,7 @@ pub use lmdb::{
|
||||
mod env;
|
||||
pub mod error;
|
||||
mod manager;
|
||||
pub mod migrate;
|
||||
mod readwrite;
|
||||
pub mod store;
|
||||
pub mod value;
|
||||
|
2
third_party/rust/rkv/src/manager.rs
vendored
2
third_party/rust/rkv/src/manager.rs
vendored
@ -38,6 +38,8 @@ use crate::error::StoreError;
|
||||
use crate::Rkv;
|
||||
|
||||
lazy_static! {
|
||||
/// A process is only permitted to have one open handle to each Rkv environment.
|
||||
/// This manager exists to enforce that constraint: don't open environments directly.
|
||||
static ref MANAGER: RwLock<Manager> = RwLock::new(Manager::new());
|
||||
}
|
||||
|
||||
|
1028
third_party/rust/rkv/src/migrate.rs
vendored
Normal file
1028
third_party/rust/rkv/src/migrate.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,9 +13,10 @@ log = "0.4"
|
||||
moz_task = { path = "../../../xpcom/rust/moz_task" }
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
rkv = "0.9.4"
|
||||
rkv = "0.9.6"
|
||||
storage_variant = { path = "../../../storage/variant" }
|
||||
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||
tempfile = "3"
|
||||
thin-vec = { version = "0.1.0", features = ["gecko-ffi"] }
|
||||
|
||||
# Get rid of failure's dependency on backtrace. Eventually
|
||||
|
@ -8,8 +8,8 @@ use nserror::{
|
||||
NS_ERROR_NULL_POINTER, NS_ERROR_UNEXPECTED,
|
||||
};
|
||||
use nsstring::nsCString;
|
||||
use rkv::StoreError;
|
||||
use std::{str::Utf8Error, string::FromUtf16Error, sync::PoisonError};
|
||||
use rkv::{migrate::MigrateError, StoreError};
|
||||
use std::{io::Error as IoError, str::Utf8Error, string::FromUtf16Error, sync::PoisonError};
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum KeyValueError {
|
||||
@ -19,6 +19,12 @@ pub enum KeyValueError {
|
||||
#[fail(display = "error converting string: {:?}", _0)]
|
||||
ConvertString(FromUtf16Error),
|
||||
|
||||
#[fail(display = "I/O error: {:?}", _0)]
|
||||
IoError(IoError),
|
||||
|
||||
#[fail(display = "migrate error: {:?}", _0)]
|
||||
MigrateError(MigrateError),
|
||||
|
||||
#[fail(display = "no interface '{}'", _0)]
|
||||
NoInterface(&'static str),
|
||||
|
||||
@ -61,12 +67,14 @@ impl From<KeyValueError> for nsresult {
|
||||
match err {
|
||||
KeyValueError::ConvertBytes(_) => NS_ERROR_FAILURE,
|
||||
KeyValueError::ConvertString(_) => NS_ERROR_FAILURE,
|
||||
KeyValueError::IoError(_) => NS_ERROR_FAILURE,
|
||||
KeyValueError::NoInterface(_) => NS_ERROR_NO_INTERFACE,
|
||||
KeyValueError::Nsresult(_, result) => result,
|
||||
KeyValueError::NullPointer => NS_ERROR_NULL_POINTER,
|
||||
KeyValueError::PoisonError => NS_ERROR_UNEXPECTED,
|
||||
KeyValueError::Read => NS_ERROR_FAILURE,
|
||||
KeyValueError::StoreError(_) => NS_ERROR_FAILURE,
|
||||
KeyValueError::MigrateError(_) => NS_ERROR_FAILURE,
|
||||
KeyValueError::UnsupportedOwned => NS_ERROR_NOT_IMPLEMENTED,
|
||||
KeyValueError::UnexpectedValue => NS_ERROR_UNEXPECTED,
|
||||
KeyValueError::UnsupportedVariant(_) => NS_ERROR_NOT_IMPLEMENTED,
|
||||
@ -74,12 +82,24 @@ impl From<KeyValueError> for nsresult {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for KeyValueError {
|
||||
fn from(err: IoError) -> KeyValueError {
|
||||
KeyValueError::IoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoreError> for KeyValueError {
|
||||
fn from(err: StoreError) -> KeyValueError {
|
||||
KeyValueError::StoreError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MigrateError> for KeyValueError {
|
||||
fn from(err: MigrateError) -> KeyValueError {
|
||||
KeyValueError::MigrateError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for KeyValueError {
|
||||
fn from(err: Utf8Error) -> KeyValueError {
|
||||
KeyValueError::ConvertBytes(err)
|
||||
|
@ -16,6 +16,7 @@ extern crate nserror;
|
||||
extern crate nsstring;
|
||||
extern crate rkv;
|
||||
extern crate storage_variant;
|
||||
extern crate tempfile;
|
||||
extern crate thin_vec;
|
||||
extern crate xpcom;
|
||||
|
||||
|
@ -13,9 +13,12 @@
|
||||
//! Since kvstore consumers use canonical paths in practice, this manager
|
||||
//! works around that bug by not doing so itself.
|
||||
|
||||
use rkv::{Rkv, StoreError};
|
||||
use error::KeyValueError;
|
||||
use lmdb::Error as LmdbError;
|
||||
use rkv::{migrate::Migrator, Rkv, StoreError};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::fs::copy;
|
||||
use std::path::{
|
||||
Path,
|
||||
PathBuf,
|
||||
@ -24,6 +27,7 @@ use std::sync::{
|
||||
Arc,
|
||||
RwLock,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
lazy_static! {
|
||||
static ref MANAGER: RwLock<Manager> = RwLock::new(Manager::new());
|
||||
@ -45,15 +49,28 @@ impl Manager {
|
||||
}
|
||||
|
||||
/// Return the open env at `path`, or create it by calling `f`.
|
||||
pub fn get_or_create<'p, F, P>(&mut self, path: P, f: F) -> Result<Arc<RwLock<Rkv>>, StoreError>
|
||||
pub fn get_or_create<'p, F, P>(&mut self, path: P, f: F) -> Result<Arc<RwLock<Rkv>>, KeyValueError>
|
||||
where
|
||||
F: FnOnce(&Path) -> Result<Rkv, StoreError>,
|
||||
F: Fn(&Path) -> Result<Rkv, StoreError>,
|
||||
P: Into<&'p Path>,
|
||||
{
|
||||
Ok(match self.environments.entry(path.into().to_path_buf()) {
|
||||
let path = path.into();
|
||||
Ok(match self.environments.entry(path.to_path_buf()) {
|
||||
Entry::Occupied(e) => e.get().clone(),
|
||||
Entry::Vacant(e) => {
|
||||
let k = Arc::new(RwLock::new(f(e.key().as_path())?));
|
||||
let env = match f(e.key().as_path()) {
|
||||
Ok(env) => env,
|
||||
Err(StoreError::LmdbError(LmdbError::Invalid)) => {
|
||||
let temp_env = tempdir()?;
|
||||
let mut migrator = Migrator::new(path)?;
|
||||
migrator.migrate(temp_env.path())?;
|
||||
copy(temp_env.path().join("data.mdb"), path.join("data.mdb"))?;
|
||||
copy(temp_env.path().join("lock.mdb"), path.join("lock.mdb"))?;
|
||||
f(e.key().as_path())?
|
||||
},
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
let k = Arc::new(RwLock::new(env));
|
||||
e.insert(k).clone()
|
||||
},
|
||||
})
|
||||
|
60
toolkit/components/kvstore/test/xpcshell/make-test-env.js
Normal file
60
toolkit/components/kvstore/test/xpcshell/make-test-env.js
Normal file
@ -0,0 +1,60 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// An xpcshell script to create a test database. Useful for creating
|
||||
// the test-env-32 and test-env-64 databases that we use to test migration
|
||||
// of databases across architecture changes.
|
||||
//
|
||||
// To create a test database, simply run this script using xpcshell:
|
||||
//
|
||||
// path/to/xpcshell path/to/make-test-env.js
|
||||
//
|
||||
// The script will create the test-env-32 or test-env-64 directory
|
||||
// (depending on the current architecture) in the current working directory,
|
||||
// create a database called "db" within it, and populate the database
|
||||
// with sample data.
|
||||
//
|
||||
// Note: you don't necessarily need to run this script on every architecture
|
||||
// for which you'd like to create a database. Once you have a database for one
|
||||
// architecture, you can use the mdb_dump and mdb_load utilities (if available
|
||||
// for your systems) to create them for others. To do so, first dump the data
|
||||
// on the original architecture:
|
||||
//
|
||||
// mdb_dump -s db path/to/original/test-env-dir > path/to/dump.txt
|
||||
//
|
||||
// Then load the data on the new architecture:
|
||||
//
|
||||
// mkdir path/to/new/test-env-dir
|
||||
// mdb_load -s db path/to/dump.txt
|
||||
|
||||
"use strict";
|
||||
|
||||
const {KeyValueService} = ChromeUtils.import("resource://gre/modules/kvstore.jsm");
|
||||
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
(async function() {
|
||||
const currentDir = await OS.File.getCurrentDirectory();
|
||||
const testEnvDir = Services.appinfo.is64Bit ? "test-env-64" : "test-env-32";
|
||||
const testEnvPath = OS.Path.join(currentDir, testEnvDir);
|
||||
await OS.File.makeDir(testEnvPath, { from: currentDir });
|
||||
|
||||
const database = await KeyValueService.getOrCreate(testEnvPath, "db");
|
||||
await database.put("int-key", 1234);
|
||||
await database.put("double-key", 56.78);
|
||||
await database.put("string-key", "Héllo, wőrld!");
|
||||
await database.put("bool-key", true);
|
||||
|
||||
scriptDone = true;
|
||||
})();
|
||||
|
||||
// Do async processing until the async function call completes.
|
||||
// From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
|
||||
let scriptDone = false;
|
||||
const mainThread = Services.tm.currentThread;
|
||||
while (!scriptDone) {
|
||||
mainThread.processNextEvent(true);
|
||||
}
|
||||
while (mainThread.hasPendingEvents()) {
|
||||
mainThread.processNextEvent(true);
|
||||
}
|
BIN
toolkit/components/kvstore/test/xpcshell/test-env-32/data.mdb
Normal file
BIN
toolkit/components/kvstore/test/xpcshell/test-env-32/data.mdb
Normal file
Binary file not shown.
BIN
toolkit/components/kvstore/test/xpcshell/test-env-32/lock.mdb
Normal file
BIN
toolkit/components/kvstore/test/xpcshell/test-env-32/lock.mdb
Normal file
Binary file not shown.
BIN
toolkit/components/kvstore/test/xpcshell/test-env-64/data.mdb
Normal file
BIN
toolkit/components/kvstore/test/xpcshell/test-env-64/data.mdb
Normal file
Binary file not shown.
BIN
toolkit/components/kvstore/test/xpcshell/test-env-64/lock.mdb
Normal file
BIN
toolkit/components/kvstore/test/xpcshell/test-env-64/lock.mdb
Normal file
Binary file not shown.
@ -5,6 +5,7 @@
|
||||
|
||||
const {KeyValueService} = ChromeUtils.import("resource://gre/modules/kvstore.jsm");
|
||||
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
@ -547,3 +548,28 @@ add_task(async function enumeration() {
|
||||
await database.delete("string-key");
|
||||
await database.delete("bool-key");
|
||||
});
|
||||
|
||||
add_task(async function migration() {
|
||||
const currentDir = await OS.File.getCurrentDirectory();
|
||||
const databaseDir = await makeDatabaseDir("migration");
|
||||
|
||||
// We're testing migration from a different architecture to our own,
|
||||
// so we choose the 32-bit database if we're a 64-bit build, and vice-versa.
|
||||
const testEnvDir = Services.appinfo.is64Bit ? "test-env-32" : "test-env-64";
|
||||
|
||||
await OS.File.copy(
|
||||
OS.Path.join(currentDir, testEnvDir, "data.mdb"),
|
||||
OS.Path.join(databaseDir, "data.mdb")
|
||||
);
|
||||
await OS.File.copy(
|
||||
OS.Path.join(currentDir, testEnvDir, "lock.mdb"),
|
||||
OS.Path.join(databaseDir, "lock.mdb")
|
||||
);
|
||||
|
||||
const database = await KeyValueService.getOrCreate(databaseDir, "db");
|
||||
|
||||
Assert.strictEqual(await database.get("int-key"), 1234);
|
||||
Assert.strictEqual(await database.get("double-key"), 56.78);
|
||||
Assert.strictEqual(await database.get("string-key"), "Héllo, wőrld!");
|
||||
Assert.strictEqual(await database.get("bool-key"), true);
|
||||
});
|
||||
|
@ -1,4 +1,7 @@
|
||||
[DEFAULT]
|
||||
skip-if = toolkit == 'android'
|
||||
support-files =
|
||||
test-env-32
|
||||
test-env-64
|
||||
|
||||
[test_kvstore.js]
|
||||
|
@ -14,8 +14,9 @@ log = "0.4"
|
||||
moz_task = { path = "../../../xpcom/rust/moz_task" }
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
rkv = "0.9.3"
|
||||
rkv = "0.9.6"
|
||||
serde_json = "1"
|
||||
tempfile = "3"
|
||||
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||
|
||||
# Get rid of failure's dependency on backtrace. Eventually
|
||||
|
@ -5,7 +5,7 @@
|
||||
use nserror::{
|
||||
nsresult, NS_ERROR_FAILURE, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_NOT_AVAILABLE, NS_ERROR_UNEXPECTED,
|
||||
};
|
||||
use rkv::StoreError as RkvStoreError;
|
||||
use rkv::{migrate::MigrateError as RkvMigrateError, StoreError as RkvStoreError};
|
||||
use serde_json::Error as SerdeJsonError;
|
||||
use std::{io::Error as IoError, str::Utf8Error, string::FromUtf16Error, sync::PoisonError};
|
||||
|
||||
@ -34,6 +34,9 @@ pub(crate) enum XULStoreError {
|
||||
#[fail(display = "poison error getting read/write lock")]
|
||||
PoisonError,
|
||||
|
||||
#[fail(display = "migrate error: {:?}", _0)]
|
||||
RkvMigrateError(RkvMigrateError),
|
||||
|
||||
#[fail(display = "store error: {:?}", _0)]
|
||||
RkvStoreError(RkvStoreError),
|
||||
|
||||
@ -60,6 +63,7 @@ impl From<XULStoreError> for nsresult {
|
||||
XULStoreError::JsonError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::NsResult(result) => result,
|
||||
XULStoreError::PoisonError => NS_ERROR_UNEXPECTED,
|
||||
XULStoreError::RkvMigrateError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::RkvStoreError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::IdAttrNameTooLong => NS_ERROR_ILLEGAL_VALUE,
|
||||
XULStoreError::Unavailable => NS_ERROR_NOT_AVAILABLE,
|
||||
@ -87,6 +91,12 @@ impl<T> From<PoisonError<T>> for XULStoreError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RkvMigrateError> for XULStoreError {
|
||||
fn from(err: RkvMigrateError) -> XULStoreError {
|
||||
XULStoreError::RkvMigrateError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RkvStoreError> for XULStoreError {
|
||||
fn from(err: RkvStoreError) -> XULStoreError {
|
||||
XULStoreError::RkvStoreError(err)
|
||||
|
@ -18,6 +18,7 @@ extern crate nserror;
|
||||
extern crate nsstring;
|
||||
extern crate rkv;
|
||||
extern crate serde_json;
|
||||
extern crate tempfile;
|
||||
#[macro_use]
|
||||
extern crate xpcom;
|
||||
|
||||
|
@ -7,16 +7,18 @@ use crate::{
|
||||
ffi::ProfileChangeObserver,
|
||||
make_key, SEPARATOR,
|
||||
};
|
||||
use lmdb::Error as LmdbError;
|
||||
use moz_task::is_main_thread;
|
||||
use nsstring::nsString;
|
||||
use rkv::{Rkv, SingleStore, StoreOptions, Value};
|
||||
use rkv::{migrate::Migrator, Rkv, SingleStore, StoreError, StoreOptions, Value};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs::{create_dir_all, remove_file, File},
|
||||
fs::{copy, create_dir_all, remove_file, File},
|
||||
path::PathBuf,
|
||||
str,
|
||||
sync::Mutex,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
use xpcom::{interfaces::nsIFile, XpCom};
|
||||
|
||||
type XULStoreCache = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
|
||||
@ -43,7 +45,27 @@ lazy_static! {
|
||||
|
||||
pub(crate) fn get_database() -> XULStoreResult<Database> {
|
||||
let xulstore_dir = get_xulstore_dir()?;
|
||||
let env = Rkv::new(xulstore_dir.as_path())?;
|
||||
let xulstore_path = xulstore_dir.as_path();
|
||||
|
||||
let env = match Rkv::new(xulstore_path) {
|
||||
Ok(env) => Ok(env),
|
||||
Err(StoreError::LmdbError(LmdbError::Invalid)) => {
|
||||
let temp_env = tempdir()?;
|
||||
let mut migrator = Migrator::new(&xulstore_path)?;
|
||||
migrator.migrate(temp_env.path())?;
|
||||
copy(
|
||||
temp_env.path().join("data.mdb"),
|
||||
xulstore_path.join("data.mdb"),
|
||||
)?;
|
||||
copy(
|
||||
temp_env.path().join("lock.mdb"),
|
||||
xulstore_path.join("lock.mdb"),
|
||||
)?;
|
||||
Rkv::new(xulstore_path)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}?;
|
||||
|
||||
let store = env.open_single("db", StoreOptions::create())?;
|
||||
|
||||
Ok(Database::new(env, store))
|
||||
|
Loading…
Reference in New Issue
Block a user