Bug 1525392 - update rkv (and LMDB) to their latest stable versions r=froydnj
authorMyk Melez <myk@mykzilla.org>
Mon, 11 Feb 2019 19:53:28 +0000
changeset 516458 5f738ddb6e4ab871f8a29f3e14fadc765c0f3129
parent 516457 787a2598c88f6033f3379e45fefea11422899276
child 516459 9f12353abfdae9c92c71b9db600ceca0c6a55f92
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1525392, 1525219
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1525392 - update rkv (and LMDB) to their latest stable versions r=froydnj Changes to rkv and LMDB crates: rkv 0.7.0 -> 0.9.1 lmdb-rkv 0.9.0 -> 0.11.2 lmdb-sys 0.8.0 -> lmdb-rkv-sys 0.8.2 Update to the LMDB C library: LMDB 0.9.21 -> 0.9.23 (+ backported patch for Mozilla build issue) Other crate dependency update: lazy_static 1.0.1 -> 1.2.0 This also removes the workaround for bug 1525219 and updates the kvstore in-tree crate (and nsIKeyValueService XPCOM API) for the rkv changes. Differential Revision: https://phabricator.services.mozilla.com/D19094
.cargo/config.in
Cargo.lock
Cargo.toml
third_party/rust/lazy_static/.cargo-checksum.json
third_party/rust/lazy_static/Cargo.toml
third_party/rust/lazy_static/README.md
third_party/rust/lazy_static/appveyor.yml
third_party/rust/lazy_static/src/core_lazy.rs
third_party/rust/lazy_static/src/inline_lazy.rs
third_party/rust/lazy_static/src/lazy.rs
third_party/rust/lazy_static/src/lib.rs
third_party/rust/lazy_static/src/nightly_lazy.rs
third_party/rust/lazy_static/tests/no_std.rs
third_party/rust/lazy_static/tests/test.rs
third_party/rust/lmdb-rkv-sys/.cargo-checksum.json
third_party/rust/lmdb-rkv-sys/Cargo.toml
third_party/rust/lmdb-rkv-sys/build.rs
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/CHANGES
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/COPYRIGHT
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/Doxyfile
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/LICENSE
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/Makefile
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/intro.doc
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/lmdb.h
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_copy.1
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_copy.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_dump.1
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_dump.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_load.1
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_load.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_stat.1
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_stat.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.h
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest2.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest3.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest4.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest5.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest6.c
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/sample-bdb.txt
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/sample-mdb.txt
third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/tooltag
third_party/rust/lmdb-rkv-sys/src/constants.rs
third_party/rust/lmdb-rkv-sys/src/ffi.rs
third_party/rust/lmdb-rkv-sys/src/lib.rs
third_party/rust/lmdb-rkv/.cargo-checksum.json
third_party/rust/lmdb-rkv/Cargo.toml
third_party/rust/lmdb-rkv/azure-pipelines-template.yml
third_party/rust/lmdb-rkv/azure-pipelines.yml
third_party/rust/lmdb-rkv/src/cursor.rs
third_party/rust/lmdb-rkv/src/environment.rs
third_party/rust/lmdb-rkv/src/lib.rs
third_party/rust/lmdb-rkv/src/transaction.rs
third_party/rust/lmdb-sys/.cargo-checksum.json
third_party/rust/lmdb-sys/Cargo.toml
third_party/rust/lmdb-sys/build.rs
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/.gitignore
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/CHANGES
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/COPYRIGHT
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/Doxyfile
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/LICENSE
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/Makefile
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/intro.doc
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/lmdb.h
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_copy.1
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_copy.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_dump.1
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_dump.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_load.1
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_load.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_stat.1
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_stat.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/midl.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/midl.h
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest2.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest3.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest4.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest5.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest6.c
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/sample-bdb.txt
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/sample-mdb.txt
third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/tooltag
third_party/rust/lmdb-sys/src/constants.rs
third_party/rust/lmdb-sys/src/ffi.rs
third_party/rust/lmdb-sys/src/lib.rs
third_party/rust/rkv/.cargo-checksum.json
third_party/rust/rkv/Cargo.toml
third_party/rust/rkv/examples/iterator.rs
third_party/rust/rkv/examples/simple-store.rs
third_party/rust/rkv/src/env.rs
third_party/rust/rkv/src/error.rs
third_party/rust/rkv/src/integer.rs
third_party/rust/rkv/src/lib.rs
third_party/rust/rkv/src/manager.rs
third_party/rust/rkv/src/readwrite.rs
third_party/rust/rkv/src/store.rs
third_party/rust/rkv/src/store/integer.rs
third_party/rust/rkv/src/store/integermulti.rs
third_party/rust/rkv/src/store/multi.rs
third_party/rust/rkv/src/store/single.rs
third_party/rust/rkv/src/value.rs
third_party/rust/rkv/tests/integer-store.rs
third_party/rust/rkv/tests/manager.rs
third_party/rust/rkv/tests/multi-integer-store.rs
toolkit/components/kvstore/Cargo.toml
toolkit/components/kvstore/nsIKeyValue.idl
toolkit/components/kvstore/src/lib.rs
toolkit/components/kvstore/src/owned_value.rs
toolkit/components/kvstore/src/task.rs
toolkit/components/kvstore/test/xpcshell/test_kvstore.js
xpcom/rust/moz_task/src/lib.rs
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -17,17 +17,12 @@ git = "https://github.com/froydnj/winapi
 branch = "aarch64"
 replace-with = "vendored-sources"
 
 [source."https://github.com/alexcrichton/cc-rs"]
 git = "https://github.com/glandium/cc-rs"
 branch = "1.0.23-clang-cl-aarch64"
 replace-with = "vendored-sources"
 
-[source."https://github.com/mozilla/lmdb-rs"]
-git = "https://github.com/mozilla/lmdb-rs"
-branch = "lmdb-sys-0.8.0-android-fix"
-replace-with = "vendored-sources"
-
 [source.vendored-sources]
 directory = '@top_srcdir@/third_party/rust'
 
 @WIN64_CARGO_LINKER_CONFIG@
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,16 +1,16 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
 [[package]]
 name = "Inflector"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "adler32"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
@@ -218,17 +218,17 @@ name = "bindgen"
 version = "0.43.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "cexpr 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "clang-sys 0.26.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -574,31 +574,31 @@ dependencies = [
 [[package]]
 name = "crossbeam-epoch"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-epoch"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-utils"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -796,17 +796,17 @@ dependencies = [
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "docopt"
 version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (git+https://github.com/servo/serde?branch=deserialize_from_enums9)",
  "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "dtoa"
@@ -828,17 +828,17 @@ dependencies = [
  "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "dwrote"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (git+https://github.com/servo/serde?branch=deserialize_from_enums9)",
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "either"
@@ -1034,17 +1034,17 @@ source = "registry+https://github.com/ru
 [[package]]
 name = "geckodriver"
 version = "0.24.0"
 dependencies = [
  "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozprofile 0.5.0",
  "mozrunner 0.9.0",
  "mozversion 0.2.0",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (git+https://github.com/servo/serde?branch=deserialize_from_enums9)",
  "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1299,17 +1299,17 @@ source = "registry+https://github.com/ru
 [[package]]
 name = "js"
 version = "0.1.4"
 dependencies = [
  "bindgen 0.43.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozjs_sys 0.0.0",
  "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "jsrust"
@@ -1343,23 +1343,23 @@ source = "registry+https://github.com/ru
 [[package]]
 name = "kvstore"
 version = "0.1.0"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "lmdb-rkv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lmdb-rkv 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "moz_task 0.1.0",
  "nserror 0.1.0",
  "nsstring 0.1.0",
  "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rkv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rkv 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "storage_variant 0.1.0",
  "xpcom 0.1.0",
 ]
 
 [[package]]
 name = "lalrpop"
 version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1407,17 +1407,17 @@ dependencies = [
 
 [[package]]
 name = "lalrpop-util"
 version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "lazy_static"
-version = "1.0.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "lazycell"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -1447,17 +1447,17 @@ dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "libudev-sys 0.1.3",
 ]
 
 [[package]]
 name = "libudev-sys"
 version = "0.1.3"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "libz-sys"
 version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
@@ -1469,28 +1469,28 @@ dependencies = [
 
 [[package]]
 name = "linked-hash-map"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "lmdb-rkv"
-version = "0.9.0"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "lmdb-sys 0.8.0 (git+https://github.com/mozilla/lmdb-rs?branch=lmdb-sys-0.8.0-android-fix)",
+ "lmdb-rkv-sys 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
-name = "lmdb-sys"
-version = "0.8.0"
-source = "git+https://github.com/mozilla/lmdb-rs?branch=lmdb-sys-0.8.0-android-fix#1295a0e2c0e3eeff2036ada60cb22d57663d0e74"
+name = "lmdb-rkv-sys"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cc 1.0.23 (git+https://github.com/glandium/cc-rs?branch=1.0.23-clang-cl-aarch64)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "lock_api"
@@ -2110,17 +2110,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "rayon-core"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "redox_syscall"
 version = "0.1.32"
@@ -2179,26 +2179,27 @@ name = "regex-syntax"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rkv"
-version = "0.7.0"
+version = "0.9.2"
 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)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "lmdb-rkv 0.9.0 (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.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.80 (git+https://github.com/servo/serde?branch=deserialize_from_enums9)",
  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ron"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2458,17 +2459,17 @@ name = "string"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "string_cache"
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -2513,17 +2514,17 @@ dependencies = [
  "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
  "num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2715,17 +2716,17 @@ dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "thread_local"
 version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "thread_profiler"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
@@ -3050,17 +3051,17 @@ source = "registry+https://github.com/ru
 name = "webdriver"
 version = "0.39.0"
 dependencies = [
  "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "http 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (git+https://github.com/servo/serde?branch=deserialize_from_enums9)",
  "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3087,17 +3088,17 @@ dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "plane-split 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3253,17 +3254,17 @@ dependencies = [
  "nsstring 0.1.0",
  "xpcom 0.1.0",
 ]
 
 [[package]]
 name = "xpcom_macros"
 version = "0.1.0"
 dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "yaml-rust"
 version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3409,26 +3410,26 @@ dependencies = [
 "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
 "checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
 "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum khronos_api 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62237e6d326bd5871cd21469323bf096de81f1618cd82cbaf5d87825335aeb49"
 "checksum lalrpop 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f7014afd5642680074fd5dcc624d544f9eabfa281cba2c3ac56c3db6d21ad1b"
 "checksum lalrpop-snap 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b85aa455529344133d7ecaaac04c01ed87f459deeaa0fe5422885e2095d8cdc"
 "checksum lalrpop-util 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2400aeebcd11259370d038c24821b93218dd2f33a53f53e9c8fcccca70be6696"
-"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739"
+"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
 "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
 "checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
 "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
 "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
 "checksum libudev 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
 "checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
 "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
-"checksum lmdb-rkv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aba42e6d930590ce6625566f2d16300add86ef11335eedba0fcf29f9f4cbd8c5"
-"checksum lmdb-sys 0.8.0 (git+https://github.com/mozilla/lmdb-rs?branch=lmdb-sys-0.8.0-android-fix)" = "<none>"
+"checksum lmdb-rkv 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1452294309db7977dc75e1e8135a8c654d9e52e04ff0c0bd06c880897a91defd"
+"checksum lmdb-rkv-sys 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96846a2e6785ec0fce6577479d18273c8e5b287e6df8a1b398b7f0f7a41cdcbb"
 "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
 "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
 "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
 "checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3"
 "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
 "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
 "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
 "checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
@@ -3483,17 +3484,17 @@ dependencies = [
 "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
 "checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
 "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
 "checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26"
 "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
 "checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3"
 "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 rkv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79504d734e64f3d9391fbcaf15ca247421da461b95b9cdebd9af5679a4cfd8c8"
+"checksum rkv 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "967f320b7357a64909224619c90c1832f4532c3f19ec24350860bd64c2c7e272"
 "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"
 "checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
 "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
 "checksum ryu 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0568787116e13c652377b6846f5931454a363a8fdf8ae50463ee40935b278b"
 "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
 "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,12 +53,11 @@ panic = "abort"
 opt-level = 2
 rpath = false
 debug-assertions = false
 panic = "abort"
 codegen-units = 1
 
 [patch.crates-io]
 libudev-sys = { path = "dom/webauthn/libudev-sys" }
-lmdb-sys = { git = "https://github.com/mozilla/lmdb-rs", branch = "lmdb-sys-0.8.0-android-fix" }
 serde_derive = { git = "https://github.com/servo/serde", branch = "deserialize_from_enums9" }
 winapi = { git = "https://github.com/froydnj/winapi-rs", branch = "aarch64" }
 cc = { git = "https://github.com/glandium/cc-rs", branch = "1.0.23-clang-cl-aarch64" }
--- a/third_party/rust/lazy_static/.cargo-checksum.json
+++ b/third_party/rust/lazy_static/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"64e1cd36c70ec172cab7e96c5faa3c670f9313f677c90db977224f9b24a7d468","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"7daae430cf5936e151386856293024e1466cddf62f27a9ff7ee3ef1511698514","appveyor.yml":"d17efb6bab4af26aa0048930de8bd3310b8d8a9961946a7962fe9fef31f96720","src/core_lazy.rs":"f20f2fc2bb751cf74622b05ec44675b6a29bfaa67d91a2e55485c665647d2904","src/lazy.rs":"a62cc96b7177a68b25e993592b08a6c706e9020fd565a55afa1fb657efa4052c","src/lib.rs":"75eee349be03ac278c1183cd861ffd0117b7ab7fffd4d46d0a1e21b9c48eab9b","src/nightly_lazy.rs":"7c00b832e907efeee16a01e1739fa536cd01b97a31d71f00d72dcabcf2e4e428","tests/no_std.rs":"2a5236bd3892a253855b4dc192f63138239165fa23b9c3421a9faa5482c780aa","tests/test.rs":"12b100f4790037ca049692d896114d6361373c5321c29e0032c290ee39f1042c"},"package":"e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739"}
\ No newline at end of file
+{"files":{"Cargo.toml":"9501e790a183ddbc845edba899bffa05f573c43fa5f79ef065ead842d056fa85","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"f2e94da8b77ac2a097dbc4b93b35912ef41b725624512d83ea968951dd5bdc7d","src/core_lazy.rs":"6b9fb6a4f553058e240756125b6b9ca43a83ed1fb72964343038ea0ea2e1af10","src/inline_lazy.rs":"2ae9a04c5bff40e80194f65b01012f0b42efa527bf717e176c68b4ca5212043c","src/lib.rs":"3defd7a82feced71862161a3c36fcff7cef3e08a51596b2e15d629b9a171a75a","tests/no_std.rs":"d68b149ee51ef5ae2b2906c0c94f5a9812d3b02811b13365c5a35e2ef90d25cf","tests/test.rs":"8e809c0f0332a3a60fca0113128cdab2cdbee92f03db523cdc4e82f4cd4b9f22"},"package":"a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"}
\ No newline at end of file
--- a/third_party/rust/lazy_static/Cargo.toml
+++ b/third_party/rust/lazy_static/Cargo.toml
@@ -7,32 +7,35 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "lazy_static"
-version = "1.0.1"
+version = "1.2.0"
 authors = ["Marvin Löbel <loebel.marvin@gmail.com>"]
+exclude = ["/.travis.yml", "/appveyor.yml"]
 description = "A macro for declaring lazily evaluated statics in Rust."
 documentation = "https://docs.rs/lazy_static"
 readme = "README.md"
 keywords = ["macro", "lazy", "static"]
 categories = ["no-std", "rust-patterns", "memory-management"]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/rust-lang-nursery/lazy-static.rs"
 [dependencies.spin]
-version = "0.4.6"
+version = "0.4.10"
+features = ["once"]
 optional = true
+default-features = false
 
 [features]
 nightly = []
-spin_no_std = ["nightly", "spin"]
+spin_no_std = ["spin"]
 [badges.appveyor]
 repository = "rust-lang-nursery/lazy-static.rs"
 
 [badges.is-it-maintained-issue-resolution]
 repository = "rust-lang-nursery/lazy-static.rs"
 
 [badges.is-it-maintained-open-issues]
 repository = "rust-lang-nursery/lazy-static.rs"
--- a/third_party/rust/lazy_static/README.md
+++ b/third_party/rust/lazy_static/README.md
@@ -4,29 +4,39 @@ lazy-static.rs
 A macro for declaring lazily evaluated statics in Rust.
 
 Using this macro, it is possible to have `static`s that require code to be
 executed at runtime in order to be initialized.
 This includes anything requiring heap allocations, like vectors or hash maps,
 as well as anything that requires non-const function calls to be computed.
 
 [![Travis-CI Status](https://travis-ci.org/rust-lang-nursery/lazy-static.rs.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/lazy-static.rs)
+[![Latest version](https://img.shields.io/crates/v/lazy_static.svg)](https://crates.io/crates/lazy_static)
+[![Documentation](https://docs.rs/lazy_static/badge.svg)](https://docs.rs/lazy_static)
+[![License](https://img.shields.io/crates/l/lazy_static.svg)](https://github.com/rust-lang-nursery/lazy-static.rs#license)
+
+## Minimum supported `rustc`
+
+`1.24.1+`
+
+This version is explicitly tested in CI and may only be bumped in new minor versions. Any changes to the supported minimum version will be called out in the release notes.
+
 
 # Getting Started
 
 [lazy-static.rs is available on crates.io](https://crates.io/crates/lazy_static).
 It is recommended to look there for the newest released version, as well as links to the newest builds of the docs.
 
 At the point of the last update of this README, the latest published version could be used like this:
 
 Add the following dependency to your Cargo manifest...
 
 ```toml
 [dependencies]
-lazy_static = "1.0"
+lazy_static = "1.2.0"
 ```
 
 ...and see the [docs](https://docs.rs/lazy_static) for how to use it.
 
 # Example
 
 ```rust
 #[macro_use]
deleted file mode 100644
--- a/third_party/rust/lazy_static/appveyor.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-environment:
-  global:
-    PROJECT_NAME: lazy_static
-    # When this was added there were revocation check failures when using the
-    # libcurl backend as libcurl checks by default, but rustup doesn't provide the
-    # switch to turn this off. Switch to Hyper which looks to not check for
-    # revocation by default like libcurl does.
-    RUSTUP_USE_REQWEST: 1
-    CARGO_HTTP_CHECK_REVOKE: false
-  matrix:
-    # Stable channel
-    - TARGET: i686-pc-windows-gnu
-      CHANNEL: stable
-    - TARGET: i686-pc-windows-msvc
-      CHANNEL: stable
-    - TARGET: x86_64-pc-windows-gnu
-      CHANNEL: stable
-    - TARGET: x86_64-pc-windows-msvc
-      CHANNEL: stable
-    # Beta channel
-    - TARGET: i686-pc-windows-gnu
-      CHANNEL: beta
-    - TARGET: i686-pc-windows-msvc
-      CHANNEL: beta
-    - TARGET: x86_64-pc-windows-gnu
-      CHANNEL: beta
-    - TARGET: x86_64-pc-windows-msvc
-      CHANNEL: beta
-    # Nightly channel
-    - TARGET: i686-pc-windows-gnu
-      CHANNEL: nightly
-    - TARGET: i686-pc-windows-msvc
-      CHANNEL: nightly
-    - TARGET: x86_64-pc-windows-gnu
-      CHANNEL: nightly
-    - TARGET: x86_64-pc-windows-msvc
-      CHANNEL: nightly
-
-# Install Rust and Cargo
-# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)
-install:
-  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
-  - rustup-init.exe -y --default-toolchain %CHANNEL% --default-host %TARGET%
-  - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
-  - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin
-  - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin
-  - rustc -V
-  - cargo -V
-
-build: false
-
-test_script:
-  - cargo build --verbose
-  - cargo test
-  - if [%CHANNEL%]==[nightly] (
-      cd compiletest &&
-      cargo clean &&
-      cargo build --verbose &&
-      cargo test &&
-      cd ../
-    )
--- a/third_party/rust/lazy_static/src/core_lazy.rs
+++ b/third_party/rust/lazy_static/src/core_lazy.rs
@@ -7,28 +7,25 @@
 
 extern crate spin;
 
 use self::spin::Once;
 
 pub struct Lazy<T: Sync>(Once<T>);
 
 impl<T: Sync> Lazy<T> {
-    #[inline(always)]
-    pub const fn new() -> Self {
-        Lazy(Once::new())
-    }
+    pub const INIT: Self = Lazy(Once::INIT);
 
     #[inline(always)]
     pub fn get<F>(&'static self, builder: F) -> &T
         where F: FnOnce() -> T
     {
         self.0.call_once(builder)
     }
 }
 
 #[macro_export]
 #[doc(hidden)]
 macro_rules! __lazy_static_create {
     ($NAME:ident, $T:ty) => {
-        static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::new();
+        static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
     }
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/lazy_static/src/inline_lazy.rs
@@ -0,0 +1,65 @@
+// Copyright 2016 lazy-static.rs Developers
+//
+// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
+// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
+// http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+extern crate core;
+extern crate std;
+
+use self::std::prelude::v1::*;
+use self::std::cell::Cell;
+use self::std::sync::Once;
+pub use self::std::sync::ONCE_INIT;
+
+// FIXME: Replace Option<T> with MaybeInitialized<T>
+pub struct Lazy<T: Sync>(Cell<Option<T>>, Once);
+
+impl<T: Sync> Lazy<T> {
+    pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT);
+
+    #[inline(always)]
+    pub fn get<F>(&'static self, f: F) -> &T
+    where
+        F: FnOnce() -> T,
+    {
+        self.1.call_once(|| {
+            self.0.set(Some(f()));
+        });
+
+        // `self.0` is guaranteed to be `Some` by this point
+        // The `Once` will catch and propegate panics
+        unsafe {
+            match *self.0.as_ptr() {
+                Some(ref x) => x,
+                None => {
+                    debug_assert!(false, "attempted to derefence an uninitialized lazy static. This is a bug");
+
+                    unreachable_unchecked()
+                },
+            }
+        }
+    }
+}
+
+unsafe impl<T: Sync> Sync for Lazy<T> {}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! __lazy_static_create {
+    ($NAME:ident, $T:ty) => {
+        static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
+    };
+}
+
+/// Polyfill for std::hint::unreachable_unchecked. There currently exists a
+/// [crate](https://docs.rs/unreachable) for an equivalent to std::hint::unreachable_unchecked, but
+/// lazy_static currently doesn't include any runtime dependencies and we've chosen to include this
+/// short polyfill rather than include a new crate in every consumer's build.
+///
+/// This should be replaced by std's version when lazy_static starts to require at least Rust 1.27.
+unsafe fn unreachable_unchecked() -> ! {
+    enum Void {}
+    match std::mem::uninitialized::<Void>() {}
+}
deleted file mode 100644
--- a/third_party/rust/lazy_static/src/lazy.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 lazy-static.rs Developers
-//
-// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
-// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
-// http://opensource.org/licenses/MIT>, at your option. This file may not be
-// copied, modified, or distributed except according to those terms.
-
-extern crate std;
-
-use self::std::prelude::v1::*;
-use self::std::sync::Once;
-pub use self::std::sync::ONCE_INIT;
-
-pub struct Lazy<T: Sync>(pub *const T, pub Once);
-
-impl<T: Sync> Lazy<T> {
-    #[inline(always)]
-    pub fn get<F>(&'static mut self, f: F) -> &T
-        where F: FnOnce() -> T
-    {
-        unsafe {
-            let r = &mut self.0;
-            self.1.call_once(|| {
-                *r = Box::into_raw(Box::new(f()));
-            });
-
-            &*self.0
-        }
-    }
-}
-
-unsafe impl<T: Sync> Sync for Lazy<T> {}
-
-#[macro_export]
-#[doc(hidden)]
-macro_rules! __lazy_static_create {
-    ($NAME:ident, $T:ty) => {
-        static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy(0 as *const $T, $crate::lazy::ONCE_INIT);
-    }
-}
--- a/third_party/rust/lazy_static/src/lib.rs
+++ b/third_party/rust/lazy_static/src/lib.rs
@@ -95,65 +95,55 @@ This crate provides two cargo features:
 - `nightly`: This uses unstable language features only available on the nightly release channel for a more optimal implementation. In practice this currently means avoiding a heap allocation per static. This feature might get deprecated at a later point once all relevant optimizations are usable from stable.
 - `spin_no_std` (implies `nightly`): This allows using this crate in a no-std environment, by depending on the standalone `spin` crate.
 
 Both features depend on unstable language features, which means
 no guarantees can be made about them in regard to SemVer stability.
 
 */
 
-#![cfg_attr(feature="spin_no_std", feature(const_fn))]
-#![cfg_attr(feature="nightly", feature(unreachable))]
-
-#![doc(html_root_url = "https://docs.rs/lazy_static/1.0.1")]
+#![doc(html_root_url = "https://docs.rs/lazy_static/1.2.0")]
 #![no_std]
 
-#[cfg(not(feature="nightly"))]
+#[cfg(not(feature = "spin_no_std"))]
+#[path="inline_lazy.rs"]
 #[doc(hidden)]
 pub mod lazy;
 
-#[cfg(all(feature="nightly", not(feature="spin_no_std")))]
-#[path="nightly_lazy.rs"]
-#[doc(hidden)]
-pub mod lazy;
-
-#[cfg(all(feature="nightly", feature="spin_no_std"))]
+#[cfg(feature = "spin_no_std")]
 #[path="core_lazy.rs"]
 #[doc(hidden)]
 pub mod lazy;
 
 #[doc(hidden)]
 pub use core::ops::Deref as __Deref;
 
-#[macro_export]
+#[macro_export(local_inner_macros)]
 #[doc(hidden)]
 macro_rules! __lazy_static_internal {
     // optional visibility restrictions are wrapped in `()` to allow for
     // explicitly passing otherwise implicit information about private items
     ($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
         __lazy_static_internal!(@MAKE TY, $(#[$attr])*, ($($vis)*), $N);
         __lazy_static_internal!(@TAIL, $N : $T = $e);
         lazy_static!($($t)*);
     };
     (@TAIL, $N:ident : $T:ty = $e:expr) => {
         impl $crate::__Deref for $N {
             type Target = $T;
-            #[allow(unsafe_code)]
             fn deref(&self) -> &$T {
-                unsafe {
-                    #[inline(always)]
-                    fn __static_ref_initialize() -> $T { $e }
+                #[inline(always)]
+                fn __static_ref_initialize() -> $T { $e }
 
-                    #[inline(always)]
-                    unsafe fn __stability() -> &'static $T {
-                        __lazy_static_create!(LAZY, $T);
-                        LAZY.get(__static_ref_initialize)
-                    }
-                    __stability()
+                #[inline(always)]
+                fn __stability() -> &'static $T {
+                    __lazy_static_create!(LAZY, $T);
+                    LAZY.get(__static_ref_initialize)
                 }
+                __stability()
             }
         }
         impl $crate::LazyStatic for $N {
             fn initialize(lazy: &Self) {
                 let _ = &**lazy;
             }
         }
     };
@@ -165,17 +155,17 @@ macro_rules! __lazy_static_internal {
         $(#[$attr])*
         $($vis)* struct $N {__private_field: ()}
         #[doc(hidden)]
         $($vis)* static $N: $N = $N {__private_field: ()};
     };
     () => ()
 }
 
-#[macro_export]
+#[macro_export(local_inner_macros)]
 macro_rules! lazy_static {
     ($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
         // use `()` to explicitly forward the information about private items
         __lazy_static_internal!($(#[$attr])* () static ref $N : $T = $e; $($t)*);
     };
     ($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
         __lazy_static_internal!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*);
     };
deleted file mode 100644
--- a/third_party/rust/lazy_static/src/nightly_lazy.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 lazy-static.rs Developers
-//
-// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
-// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
-// http://opensource.org/licenses/MIT>, at your option. This file may not be
-// copied, modified, or distributed except according to those terms.
-
-extern crate std;
-extern crate core;
-
-use self::std::prelude::v1::*;
-use self::std::sync::Once;
-pub use self::std::sync::ONCE_INIT;
-
-pub struct Lazy<T: Sync>(pub Option<T>, pub Once);
-
-impl<T: Sync> Lazy<T> {
-    #[inline(always)]
-    pub fn get<F>(&'static mut self, f: F) -> &T
-        where F: FnOnce() -> T
-    {
-        {
-            let r = &mut self.0;
-            self.1.call_once(|| {
-                *r = Some(f());
-            });
-        }
-        unsafe {
-            match self.0 {
-                Some(ref x) => x,
-                None => core::hint::unreachable_unchecked(),
-            }
-        }
-    }
-}
-
-unsafe impl<T: Sync> Sync for Lazy<T> {}
-
-#[macro_export]
-#[doc(hidden)]
-macro_rules! __lazy_static_create {
-    ($NAME:ident, $T:ty) => {
-        static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy(None, $crate::lazy::ONCE_INIT);
-    }
-}
--- a/third_party/rust/lazy_static/tests/no_std.rs
+++ b/third_party/rust/lazy_static/tests/no_std.rs
@@ -1,10 +1,9 @@
 #![cfg(feature="spin_no_std")]
-#![feature(const_fn)]
 
 #![no_std]
 
 #[macro_use]
 extern crate lazy_static;
 
 lazy_static! {
     /// Documentation!
--- a/third_party/rust/lazy_static/tests/test.rs
+++ b/third_party/rust/lazy_static/tests/test.rs
@@ -1,10 +1,8 @@
-#![cfg_attr(feature="nightly", feature(const_fn))]
-
 #[macro_use]
 extern crate lazy_static;
 use std::collections::HashMap;
 
 lazy_static! {
     /// Documentation!
     pub static ref NUMBER: u32 = times_two(3);
 
new file mode 100644
--- /dev/null
+++ b/third_party/rust/lmdb-rkv-sys/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{"Cargo.toml":"89945728564108bb284dc515a9048ffaae5016b58ea8781d9f6af7e6d4ce9000","build.rs":"4204dd0891e2eca08cb7cbcccfb36d1bdac137651c383f4a36368478fd836402","lmdb/libraries/liblmdb/CHANGES":"12f41155ee12375b7ddeabd2f793399d3216e92bc44f1122d6f305b7637c2fec","lmdb/libraries/liblmdb/COPYRIGHT":"7cf04234accacc7b41b73fe6b3b19759fa49dee1ce705a9fa6533900400e4cca","lmdb/libraries/liblmdb/Doxyfile":"5545f6b049040ce58e6d1a603eaea6b7fb8ae92459f2ab8d3bcbacabcce1014d","lmdb/libraries/liblmdb/LICENSE":"310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569","lmdb/libraries/liblmdb/Makefile":"60b5f574e6642602f692a95956da61c588a265ad50b8059960c230b9e6aaf4fd","lmdb/libraries/liblmdb/intro.doc":"9442e0db4fc9c70f058c43545e710476d8d5a80b959d20f4381240fd50c6b843","lmdb/libraries/liblmdb/lmdb.h":"a8194f7a061f4168d99de3c80464de5d279b2c851c24fdd102f1f14dd5828a77","lmdb/libraries/liblmdb/mdb.c":"87d29ea006d0c4dc31a1bc960e37db33c971abe8b3e73de5041813e0333ad8b5","lmdb/libraries/liblmdb/mdb_copy.1":"3a6a8a7a91e1bd42dc4d2a0188ff62d699ff2b3b097a670f30681decf63f22f3","lmdb/libraries/liblmdb/mdb_copy.c":"d3d412a770a5c3afeb88c44b4acdde0f0b985cde22497198e8f38296281cdddd","lmdb/libraries/liblmdb/mdb_dump.1":"9257be883c7fcfcbd61003cc730f7c0900fa8f6feba074c8c1e46634a257b13a","lmdb/libraries/liblmdb/mdb_dump.c":"b046cffcd997254e6daea47a2d7fb74f9d23282174cbb1e3bf9f5fb51a90fe64","lmdb/libraries/liblmdb/mdb_load.1":"ea927473245a4a7777ba687aa26baf7f0951fb620daf82b8d730a090185b2bbc","lmdb/libraries/liblmdb/mdb_load.c":"4f722613c65350315db23060be98584fb572978108885dab271101ba7187dca4","lmdb/libraries/liblmdb/mdb_stat.1":"c0a70d96b4b2d32e73301383d9d5620bc0bbbefb019bfd54f32088dfd4bc921a","lmdb/libraries/liblmdb/mdb_stat.c":"e6405fa191d784ecfa8eb8d1f153a58facc49a8f5a2c891a93802e67acc4861e","lmdb/libraries/liblmdb/midl.c":"92b0933c7736443448018a93f9588e9e26ae2e242f91b19211dea9ed3ab91141","lmdb/libraries/liblmdb/midl.h":"8e01e6c3b1cbd2e93a5a95686a6dd0ca43878b2b8cf0ba53962ccfe355605354","lmdb/libraries/liblmdb/mtest.c":"89ab9ac8bf1e14a9f32a33757c4b3254e4984e0f24e5a302e2d126eb2c86f6db","lmdb/libraries/liblmdb/mtest2.c":"076b00395fe1461dd9577f7bb5567908ce50cf470efbf652787e6fe1dc2fb68c","lmdb/libraries/liblmdb/mtest3.c":"51b9a055e123bd0757ee3082cc6864c836969cf630e646a9cc34e01398c20634","lmdb/libraries/liblmdb/mtest4.c":"b0a725405d80bda6ab95b3ecf410ae330ab8df7a081ca81dd6ea1f8db87642e9","lmdb/libraries/liblmdb/mtest5.c":"7f3b06ca3833315ea4c70d5e91feb1b677f6949f105f4f89d96c3ac35e104f2f","lmdb/libraries/liblmdb/mtest6.c":"e4d7880c36547ebf33bc020046730bf2c075c53aaacd5c876152cc5ae7ab5e6c","lmdb/libraries/liblmdb/sample-bdb.txt":"153d84f8fc49a3abba53ed52d5a41c8d6d4698753a10bbe0689a9e65d3513513","lmdb/libraries/liblmdb/sample-mdb.txt":"1f77385786cffdf72b33da06a91a444fe2827673c3627f89110903a8fe012795","lmdb/libraries/liblmdb/tooltag":"4734c6dc1fa7aec8c2e9646bd04bc5218ef6a03ad83a3b18de2ac4069eb94120","src/constants.rs":"af67740b5acccdc71b2267ec051bb60e5433c4f0313fe16dc0627376a52dcdff","src/ffi.rs":"caa9bbfb3868a7a9e9ad822d775e60ffa8c8c2f2450ac4ed403a93ddb7547899","src/lib.rs":"1d3222feec7b2b4e0902c3b9d7d37d51f5ae5cce8a3ff3dcf0256b7d78f0fecb"},"package":"96846a2e6785ec0fce6577479d18273c8e5b287e6df8a1b398b7f0f7a41cdcbb"}
\ No newline at end of file
copy from third_party/rust/lmdb-rkv/Cargo.toml
copy to third_party/rust/lmdb-rkv-sys/Cargo.toml
--- a/third_party/rust/lmdb-rkv/Cargo.toml
+++ b/third_party/rust/lmdb-rkv-sys/Cargo.toml
@@ -6,42 +6,26 @@
 # to registry (e.g. crates.io) dependencies
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
-name = "lmdb-rkv"
-version = "0.9.0"
+name = "lmdb-rkv-sys"
+version = "0.8.2"
 authors = ["Dan Burkert <dan@danburkert.com>"]
-description = "Idiomatic and safe LMDB wrapper."
-documentation = "https://docs.rs/lmdb-rkv"
-readme = "README.md"
+build = "build.rs"
+description = "Rust bindings for liblmdb."
+documentation = "https://docs.rs/lmdb-rkv-sys"
+readme = "../README.md"
 keywords = ["LMDB", "database", "storage-engine", "bindings", "library"]
-categories = ["database"]
+categories = ["database", "external-ffi-bindings"]
 license = "Apache-2.0"
 repository = "https://github.com/mozilla/lmdb-rs.git"
-
-[lib]
-name = "lmdb"
-[dependencies.bitflags]
+[dependencies.libc]
+version = "0.2"
+[build-dependencies.cc]
 version = "1"
 
-[dependencies.libc]
-version = "0.2"
-
-[dependencies.lmdb-sys]
-version = "0.8.0"
-[dev-dependencies.byteorder]
-version = "1.0"
-
-[dev-dependencies.rand]
-version = "0.4"
-
-[dev-dependencies.tempdir]
-version = "0.3"
-[badges.appveyor]
-repository = "mozilla/lmdb-rs"
-
-[badges.travis-ci]
-repository = "mozilla/lmdb-rs"
+[build-dependencies.pkg-config]
+version = "0.3.2"
rename from third_party/rust/lmdb-sys/build.rs
rename to third_party/rust/lmdb-rkv-sys/build.rs
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/CHANGES
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/CHANGES
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/CHANGES
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/CHANGES
@@ -1,10 +1,25 @@
 LMDB 0.9 Change Log
 
+LMDB 0.9.23 Release (2018/12/19)
+	ITS#8756 Fix loose pages in dirty list
+	ITS#8831 Fix mdb_load flag init
+	ITS#8844 Fix mdb_env_close in forked process
+	Documentation
+		ITS#8857 mdb_cursor_del doesn't invalidate cursor
+		ITS#8908 GET_MULTIPLE etc don't change passed in key
+
+LMDB 0.9.22 Release (2018/03/22)
+	Fix MDB_DUPSORT alignment bug (ITS#8819)
+	Fix regression with new db from 0.9.19 (ITS#8760)
+	Fix liblmdb to build on Solaris (ITS#8612)
+	Fix delete behavior with DUPSORT DB (ITS#8622)
+	Fix mdb_cursor_get/mdb_cursor_del behavior (ITS#8722)
+
 LMDB 0.9.21 Release (2017/06/01)
 	Fix xcursor after cursor_del (ITS#8622)
 
 LMDB 0.9.20 (Withdrawn)
 	Fix mdb_load with escaped plaintext (ITS#8558)
 	Fix mdb_cursor_last / mdb_put interaction (ITS#8557)
 
 LMDB 0.9.19 Release (2016/12/28)
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/COPYRIGHT
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/COPYRIGHT
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/COPYRIGHT
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/COPYRIGHT
@@ -1,9 +1,9 @@
-Copyright 2011-2017 Howard Chu, Symas Corp.
+Copyright 2011-2018 Howard Chu, Symas Corp.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted only as authorized by the OpenLDAP
 Public License.
 
 A copy of this license is available in the file LICENSE in the
 top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/Doxyfile
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/Doxyfile
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/LICENSE
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/LICENSE
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/Makefile
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/Makefile
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/intro.doc
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/intro.doc
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/intro.doc
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/intro.doc
@@ -1,10 +1,10 @@
 /*
- * Copyright 2015-2017 Howard Chu, Symas Corp.
+ * Copyright 2015-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/lmdb.h
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/lmdb.h
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/lmdb.h
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/lmdb.h
@@ -130,17 +130,17 @@
  *	  possibly memory map sync, and certainly sync between programs
  *	  on different hosts.
  *
  *	- Opening a database can fail if another process is opening or
  *	  closing it at exactly the same time.
  *
  *	@author	Howard Chu, Symas Corporation.
  *
- *	@copyright Copyright 2011-2017 Howard Chu, Symas Corp. All rights reserved.
+ *	@copyright Copyright 2011-2018 Howard Chu, Symas Corp. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
  * <http://www.OpenLDAP.org/license.html>.
@@ -195,27 +195,27 @@ typedef int mdb_filehandle_t;
 /** @defgroup Version Version Macros
  *	@{
  */
 /** Library major version */
 #define MDB_VERSION_MAJOR	0
 /** Library minor version */
 #define MDB_VERSION_MINOR	9
 /** Library patch version */
-#define MDB_VERSION_PATCH	21
+#define MDB_VERSION_PATCH	23
 
 /** Combine args a,b,c into a single integer for easy version comparisons */
 #define MDB_VERINT(a,b,c)	(((a) << 24) | ((b) << 16) | (c))
 
 /** The full library version as a single integer */
 #define MDB_VERSION_FULL	\
 	MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH)
 
 /** The release date of this library version */
-#define MDB_VERSION_DATE	"June 1, 2017"
+#define MDB_VERSION_DATE	"December 19, 2018"
 
 /** A stringifier for the version info */
 #define MDB_VERSTR(a,b,c,d)	"LMDB " #a "." #b "." #c ": (" d ")"
 
 /** A helper for the stringifier macro */
 #define MDB_VERFOO(a,b,c,d)	MDB_VERSTR(a,b,c,d)
 
 /** The full library version as a C string */
@@ -365,37 +365,37 @@ typedef void (MDB_rel_func)(MDB_val *ite
  */
 typedef enum MDB_cursor_op {
 	MDB_FIRST,				/**< Position at first key/data item */
 	MDB_FIRST_DUP,			/**< Position at first data item of current key.
 								Only for #MDB_DUPSORT */
 	MDB_GET_BOTH,			/**< Position at key/data pair. Only for #MDB_DUPSORT */
 	MDB_GET_BOTH_RANGE,		/**< position at key, nearest data. Only for #MDB_DUPSORT */
 	MDB_GET_CURRENT,		/**< Return key/data at current cursor position */
-	MDB_GET_MULTIPLE,		/**< Return key and up to a page of duplicate data items
+	MDB_GET_MULTIPLE,		/**< Return up to a page of duplicate data items
 								from current cursor position. Move cursor to prepare
 								for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
 	MDB_LAST,				/**< Position at last key/data item */
 	MDB_LAST_DUP,			/**< Position at last data item of current key.
 								Only for #MDB_DUPSORT */
 	MDB_NEXT,				/**< Position at next data item */
 	MDB_NEXT_DUP,			/**< Position at next data item of current key.
 								Only for #MDB_DUPSORT */
-	MDB_NEXT_MULTIPLE,		/**< Return key and up to a page of duplicate data items
+	MDB_NEXT_MULTIPLE,		/**< Return up to a page of duplicate data items
 								from next cursor position. Move cursor to prepare
 								for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
 	MDB_NEXT_NODUP,			/**< Position at first data item of next key */
 	MDB_PREV,				/**< Position at previous data item */
 	MDB_PREV_DUP,			/**< Position at previous data item of current key.
 								Only for #MDB_DUPSORT */
 	MDB_PREV_NODUP,			/**< Position at last data item of previous key */
 	MDB_SET,				/**< Position at specified key */
 	MDB_SET_KEY,			/**< Position at specified key, return key + data */
 	MDB_SET_RANGE,			/**< Position at first key greater than or equal to specified key. */
-	MDB_PREV_MULTIPLE		/**< Position at previous page and return key and up to
+	MDB_PREV_MULTIPLE		/**< Position at previous page and return up to
 								a page of duplicate data items. Only for #MDB_DUPFIXED */
 } MDB_cursor_op;
 
 /** @defgroup  errors	Return Codes
  *
  *	BerkeleyDB uses -30800 to -30999, we'll go under them
  *	@{
  */
@@ -1505,16 +1505,20 @@ int  mdb_cursor_get(MDB_cursor *cursor, 
 	 * </ul>
 	 */
 int  mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data,
 				unsigned int flags);
 
 	/** @brief Delete current key/data pair
 	 *
 	 * This function deletes the key/data pair to which the cursor refers.
+	 * This does not invalidate the cursor, so operations such as MDB_NEXT
+	 * can still be used on it.
+	 * Both MDB_NEXT and MDB_GET_CURRENT will return the same record after
+	 * this operation.
 	 * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
 	 * @param[in] flags Options for this operation. This parameter
 	 * must be set to 0 or one of the values described here.
 	 * <ul>
 	 *	<li>#MDB_NODUPDATA - delete all of the data items for the current key.
 	 *		This flag may only be specified if the database was opened with #MDB_DUPSORT.
 	 * </ul>
 	 * @return A non-zero error value on failure and 0 on success. Some possible
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb.c
@@ -1,16 +1,16 @@
 /** @file mdb.c
  *	@brief Lightning memory-mapped database library
  *
  *	A Btree-based database management library modeled loosely on the
  *	BerkeleyDB API, but much simplified.
  */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
@@ -108,16 +108,20 @@ typedef SSIZE_T	ssize_t;
 #else
 #include <unistd.h>
 #endif
 
 #if defined(__sun) || defined(__ANDROID__)
 /* Most platforms have posix_memalign, older may only have memalign */
 #define HAVE_MEMALIGN	1
 #include <malloc.h>
+/* On Solaris, we need the POSIX sigwait function */
+#if defined (__sun)
+# define _POSIX_PTHREAD_SEMANTICS	1
+#endif
 #endif
 
 #if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER))
 #include <netinet/in.h>
 #include <resolv.h>	/* defines BYTE_ORDER on HPUX and Solaris */
 #endif
 
 #if defined(__APPLE__) || defined (BSD) || defined(__FreeBSD_kernel__)
@@ -1217,27 +1221,29 @@ typedef struct MDB_xcursor {
 	/** The database record for this Dup DB */
 	MDB_db	mx_db;
 	/**	The auxiliary DB record for this Dup DB */
 	MDB_dbx	mx_dbx;
 	/** The @ref mt_dbflag for this Dup DB */
 	unsigned char mx_dbflag;
 } MDB_xcursor;
 
-	/** Check if there is an inited xcursor, so #XCURSOR_REFRESH() is proper */
+	/** Check if there is an inited xcursor */
 #define XCURSOR_INITED(mc) \
 	((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
 
-	/** Update sub-page pointer, if any, in \b mc->mc_xcursor.  Needed
+	/** Update the xcursor's sub-page pointer, if any, in \b mc.  Needed
 	 *	when the node which contains the sub-page may have moved.  Called
-	 *	with \b mp = mc->mc_pg[mc->mc_top], \b ki = mc->mc_ki[mc->mc_top].
+	 *	with leaf page \b mp = mc->mc_pg[\b top].
 	 */
-#define XCURSOR_REFRESH(mc, mp, ki) do { \
+#define XCURSOR_REFRESH(mc, top, mp) do { \
 	MDB_page *xr_pg = (mp); \
-	MDB_node *xr_node = NODEPTR(xr_pg, ki); \
+	MDB_node *xr_node; \
+	if (!XCURSOR_INITED(mc) || (mc)->mc_ki[top] >= NUMKEYS(xr_pg)) break; \
+	xr_node = NODEPTR(xr_pg, (mc)->mc_ki[top]); \
 	if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \
 		(mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \
 } while (0)
 
 	/** State of FreeDB old pages, stored in the MDB_env */
 typedef struct MDB_pgstate {
 	pgno_t		*mf_pghead;	/**< Reclaimed freeDB pages, or NULL before use */
 	txnid_t		mf_pglast;	/**< ID of last used record, or 0 if !mf_pghead */
@@ -2480,18 +2486,18 @@ done:
 				m3->mc_pg[mc->mc_top] = np;
 		}
 	} else {
 		for (; m2; m2=m2->mc_next) {
 			if (m2->mc_snum < mc->mc_snum) continue;
 			if (m2 == mc) continue;
 			if (m2->mc_pg[mc->mc_top] == mp) {
 				m2->mc_pg[mc->mc_top] = np;
-				if (XCURSOR_INITED(m2) && IS_LEAF(np))
-					XCURSOR_REFRESH(m2, np, m2->mc_ki[mc->mc_top]);
+				if (IS_LEAF(np))
+					XCURSOR_REFRESH(m2, mc->mc_top, np);
 			}
 		}
 	}
 	return 0;
 
 fail:
 	txn->mt_flags |= MDB_TXN_ERROR;
 	return rc;
@@ -3083,20 +3089,51 @@ mdb_freelist_save(MDB_txn *txn)
 			return rc;
 	}
 
 	if (!env->me_pghead && txn->mt_loose_pgs) {
 		/* Put loose page numbers in mt_free_pgs, since
 		 * we may be unable to return them to me_pghead.
 		 */
 		MDB_page *mp = txn->mt_loose_pgs;
+		MDB_ID2 *dl = txn->mt_u.dirty_list;
+		unsigned x;
 		if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0)
 			return rc;
-		for (; mp; mp = NEXT_LOOSE_PAGE(mp))
+		for (; mp; mp = NEXT_LOOSE_PAGE(mp)) {
 			mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
+			/* must also remove from dirty list */
+			if (txn->mt_flags & MDB_TXN_WRITEMAP) {
+				for (x=1; x<=dl[0].mid; x++)
+					if (dl[x].mid == mp->mp_pgno)
+						break;
+				mdb_tassert(txn, x <= dl[0].mid);
+			} else {
+				x = mdb_mid2l_search(dl, mp->mp_pgno);
+				mdb_tassert(txn, dl[x].mid == mp->mp_pgno);
+			}
+			dl[x].mptr = NULL;
+			mdb_dpage_free(env, mp);
+		}
+		{
+			/* squash freed slots out of the dirty list */
+			unsigned y;
+			for (y=1; dl[y].mptr && y <= dl[0].mid; y++);
+			if (y <= dl[0].mid) {
+				for(x=y, y++;;) {
+					while (!dl[y].mptr && y <= dl[0].mid) y++;
+					if (y > dl[0].mid) break;
+					dl[x++] = dl[y++];
+				}
+				dl[0].mid = x-1;
+			} else {
+				/* all slots freed */
+				dl[0].mid = 0;
+			}
+		}
 		txn->mt_loose_pgs = NULL;
 		txn->mt_loose_count = 0;
 	}
 
 	/* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */
 	clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP))
 		? SSIZE_MAX : maxfree_1pg;
 
@@ -5052,17 +5089,17 @@ mdb_env_close0(MDB_env *env, int excl)
 	if (env->me_map) {
 		munmap(env->me_map, env->me_mapsize);
 	}
 	if (env->me_mfd != INVALID_HANDLE_VALUE)
 		(void) close(env->me_mfd);
 	if (env->me_fd != INVALID_HANDLE_VALUE)
 		(void) close(env->me_fd);
 	if (env->me_txns) {
-		MDB_PID_T pid = env->me_pid;
+		MDB_PID_T pid = getpid();
 		/* Clearing readers is done in this function because
 		 * me_txkey with its destructor must be disabled first.
 		 *
 		 * We skip the the reader mutex, so we touch only
 		 * data owned by this process (me_close_readers and
 		 * our readers), and clear each reader atomically.
 		 */
 		for (i = env->me_close_readers; --i >= 0; )
@@ -6417,16 +6454,21 @@ fetchm:
 		if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) {
 			rc = EINVAL;
 			break;
 		}
 		if (mc->mc_xcursor == NULL) {
 			rc = MDB_INCOMPATIBLE;
 			break;
 		}
+		if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) {
+			mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]);
+			rc = MDB_NOTFOUND;
+			break;
+		}
 		{
 			MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
 			if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
 				MDB_GET_KEY(leaf, key);
 				rc = mdb_node_read(mc, leaf, data);
 				break;
 			}
 		}
@@ -6783,18 +6825,19 @@ prep_subDB:
 				mp->mp_pad   = fp->mp_pad;
 				mp->mp_lower = fp->mp_lower;
 				mp->mp_upper = fp->mp_upper + offset;
 				if (fp_flags & P_LEAF2) {
 					memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad);
 				} else {
 					memcpy((char *)mp + mp->mp_upper + PAGEBASE, (char *)fp + fp->mp_upper + PAGEBASE,
 						olddata.mv_size - fp->mp_upper - PAGEBASE);
+					memcpy((char *)(&mp->mp_ptrs), (char *)(&fp->mp_ptrs), NUMKEYS(fp) * sizeof(mp->mp_ptrs[0]));
 					for (i=0; i<NUMKEYS(fp); i++)
-						mp->mp_ptrs[i] = fp->mp_ptrs[i] + offset;
+						mp->mp_ptrs[i] += offset;
 				}
 			}
 
 			rdata = &xdata;
 			flags |= F_DUPDATA;
 			do_sub = 1;
 			if (!insert_key)
 				mdb_node_del(mc, 0);
@@ -6913,18 +6956,17 @@ new_sub:
 				if (mc->mc_flags & C_SUB)
 					m3 = &m2->mc_xcursor->mx_cursor;
 				else
 					m3 = m2;
 				if (m3 == mc || m3->mc_snum < mc->mc_snum || m3->mc_pg[i] != mp) continue;
 				if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) {
 					m3->mc_ki[i]++;
 				}
-				if (XCURSOR_INITED(m3))
-					XCURSOR_REFRESH(m3, mp, m3->mc_ki[i]);
+				XCURSOR_REFRESH(m3, i, mp);
 			}
 		}
 	}
 
 	if (rc == MDB_SUCCESS) {
 		/* Now store the actual data in the child DB. Note that we're
 		 * storing the user data in the keys field, so there are strict
 		 * size limits on dupdata. The actual data fields of the child
@@ -6956,26 +6998,25 @@ put_sub:
 				dkey.mv_size = 0;
 			}
 			if (!(leaf->mn_flags & F_SUBDATA) || sub_root) {
 				/* Adjust other cursors pointing to mp */
 				MDB_cursor *m2;
 				MDB_xcursor *mx = mc->mc_xcursor;
 				unsigned i = mc->mc_top;
 				MDB_page *mp = mc->mc_pg[i];
-				int nkeys = NUMKEYS(mp);
 
 				for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
 					if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
 					if (!(m2->mc_flags & C_INITIALIZED)) continue;
 					if (m2->mc_pg[i] == mp) {
 						if (m2->mc_ki[i] == mc->mc_ki[i]) {
 							mdb_xcursor_init2(m2, mx, new_dupdata);
-						} else if (!insert_key && m2->mc_ki[i] < nkeys) {
-							XCURSOR_REFRESH(m2, mp, m2->mc_ki[i]);
+						} else if (!insert_key) {
+							XCURSOR_REFRESH(m2, i, mp);
 						}
 					}
 				}
 			}
 			ecount = mc->mc_xcursor->mx_db.md_entries;
 			if (flags & MDB_APPENDDUP)
 				xflags |= MDB_APPEND;
 			rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags);
@@ -7070,22 +7111,17 @@ mdb_cursor_del(MDB_cursor *mc, unsigned 
 					mdb_node_shrink(mp, mc->mc_ki[mc->mc_top]);
 					leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
 					mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
 					/* fix other sub-DB cursors pointed at fake pages on this page */
 					for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
 						if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
 						if (!(m2->mc_flags & C_INITIALIZED)) continue;
 						if (m2->mc_pg[mc->mc_top] == mp) {
-							MDB_node *n2 = leaf;
-							if (m2->mc_ki[mc->mc_top] != mc->mc_ki[mc->mc_top]) {
-								n2 = NODEPTR(mp, m2->mc_ki[mc->mc_top]);
-								if (n2->mn_flags & F_SUBDATA) continue;
-							}
-							m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(n2);
+							XCURSOR_REFRESH(m2, mc->mc_top, mp);
 						}
 					}
 				}
 				mc->mc_db->md_entries--;
 				return rc;
 			} else {
 				mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
 			}
@@ -7921,18 +7957,18 @@ mdb_node_move(MDB_cursor *csrc, MDB_curs
 				}
 				if (m3 !=csrc &&
 					m3->mc_pg[csrc->mc_top] == mps &&
 					m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) {
 					m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
 					m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
 					m3->mc_ki[csrc->mc_top-1]++;
 				}
-				if (XCURSOR_INITED(m3) && IS_LEAF(mps))
-					XCURSOR_REFRESH(m3, m3->mc_pg[csrc->mc_top], m3->mc_ki[csrc->mc_top]);
+				if (IS_LEAF(mps))
+					XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]);
 			}
 		} else
 		/* Adding on the right, bump others down */
 		{
 			for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
 				if (csrc->mc_flags & C_SUB)
 					m3 = &m2->mc_xcursor->mx_cursor;
 				else
@@ -7943,18 +7979,18 @@ mdb_node_move(MDB_cursor *csrc, MDB_curs
 				if (m3->mc_pg[csrc->mc_top] == mps) {
 					if (!m3->mc_ki[csrc->mc_top]) {
 						m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
 						m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
 						m3->mc_ki[csrc->mc_top-1]--;
 					} else {
 						m3->mc_ki[csrc->mc_top]--;
 					}
-					if (XCURSOR_INITED(m3) && IS_LEAF(mps))
-						XCURSOR_REFRESH(m3, m3->mc_pg[csrc->mc_top], m3->mc_ki[csrc->mc_top]);
+					if (IS_LEAF(mps))
+						XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]);
 				}
 			}
 		}
 	}
 
 	/* Update the parent separators.
 	 */
 	if (csrc->mc_ki[csrc->mc_top] == 0) {
@@ -8145,18 +8181,18 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cur
 			if (m3->mc_pg[top] == psrc) {
 				m3->mc_pg[top] = pdst;
 				m3->mc_ki[top] += nkeys;
 				m3->mc_ki[top-1] = cdst->mc_ki[top-1];
 			} else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] &&
 				m3->mc_ki[top-1] > csrc->mc_ki[top-1]) {
 				m3->mc_ki[top-1]--;
 			}
-			if (XCURSOR_INITED(m3) && IS_LEAF(psrc))
-				XCURSOR_REFRESH(m3, m3->mc_pg[top], m3->mc_ki[top]);
+			if (IS_LEAF(psrc))
+				XCURSOR_REFRESH(m3, top, m3->mc_pg[top]);
 		}
 	}
 	{
 		unsigned int snum = cdst->mc_snum;
 		uint16_t depth = cdst->mc_db->md_depth;
 		mdb_cursor_pop(cdst);
 		rc = mdb_rebalance(cdst);
 		/* Did the tree height change? */
@@ -8408,18 +8444,17 @@ mdb_cursor_del0(MDB_cursor *mc)
 					if (mc->mc_db->md_flags & MDB_DUPSORT) {
 						/* Sub-cursor referred into dataset which is gone */
 						m3->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
 					}
 					continue;
 				} else if (m3->mc_ki[mc->mc_top] > ki) {
 					m3->mc_ki[mc->mc_top]--;
 				}
-				if (XCURSOR_INITED(m3))
-					XCURSOR_REFRESH(m3, m3->mc_pg[mc->mc_top], m3->mc_ki[mc->mc_top]);
+				XCURSOR_REFRESH(m3, mc->mc_top, mp);
 			}
 		}
 	}
 	rc = mdb_rebalance(mc);
 
 	if (rc == MDB_SUCCESS) {
 		/* DB is totally empty now, just bail out.
 		 * Other cursors adjustments were already done
@@ -8456,18 +8491,20 @@ mdb_cursor_del0(MDB_cursor *mc)
 						 * If the xcursor was not initd it must be reinited.
 						 * Else if node points to a subDB, nothing is needed.
 						 * Else (xcursor was initd, not a subDB) needs mc_pg[0] reset.
 						 */
 						if (node->mn_flags & F_DUPDATA) {
 							if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
 								if (!(node->mn_flags & F_SUBDATA))
 									m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node);
-							} else
+							} else {
 								mdb_xcursor_init1(m3, node);
+								m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL;
+							}
 						}
 					}
 				}
 			}
 		}
 		mc->mc_flags |= C_DEL;
 	}
 
@@ -8943,18 +8980,18 @@ mdb_page_split(MDB_cursor *mc, MDB_val *
 						m3->mc_ki[i] = mn.mc_ki[i];
 						m3->mc_pg[i] = mn.mc_pg[i];
 					}
 				}
 			} else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] &&
 				m3->mc_ki[ptop] >= mc->mc_ki[ptop]) {
 				m3->mc_ki[ptop]++;
 			}
-			if (XCURSOR_INITED(m3) && IS_LEAF(mp))
-				XCURSOR_REFRESH(m3, m3->mc_pg[mc->mc_top], m3->mc_ki[mc->mc_top]);
+			if (IS_LEAF(mp))
+				XCURSOR_REFRESH(m3, mc->mc_top, m3->mc_pg[mc->mc_top]);
 		}
 	}
 	DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp)));
 
 done:
 	if (copy)					/* tmp page */
 		mdb_page_free(env, copy);
 	if (rc)
@@ -9749,18 +9786,21 @@ int mdb_dbi_open(MDB_txn *txn, const cha
 	key.mv_data = (void *)name;
 	mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
 	rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact);
 	if (rc == MDB_SUCCESS) {
 		/* make sure this is actually a DB */
 		MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
 		if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
 			return MDB_INCOMPATIBLE;
-	} else if (! (rc == MDB_NOTFOUND && (flags & MDB_CREATE))) {
-		return rc;
+	} else {
+		if (rc != MDB_NOTFOUND || !(flags & MDB_CREATE))
+			return rc;
+		if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+			return EACCES;
 	}
 
 	/* Done here so we cannot fail after creating a new DB */
 	if ((namedup = strdup(name)) == NULL)
 		return ENOMEM;
 
 	if (rc) {
 		/* MDB_NOTFOUND and MDB_CREATE: Create new DB */
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_copy.1
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_copy.1
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_copy.1
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_copy.1
@@ -1,10 +1,10 @@
 .TH MDB_COPY 1 "2014/07/01" "LMDB 0.9.14"
-.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copyright 2012-2018 Howard Chu, Symas Corp. All Rights Reserved.
 .\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
 .SH NAME
 mdb_copy \- LMDB environment copy tool
 .SH SYNOPSIS
 .B mdb_copy
 [\c
 .BR \-V ]
 [\c
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_copy.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_copy.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_copy.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_copy.c
@@ -1,11 +1,11 @@
 /* mdb_copy.c - memory-mapped database backup tool */
 /*
- * Copyright 2012-2017 Howard Chu, Symas Corp.
+ * Copyright 2012-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_dump.1
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_dump.1
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_dump.1
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_dump.1
@@ -1,10 +1,10 @@
 .TH MDB_DUMP 1 "2015/09/30" "LMDB 0.9.17"
-.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copyright 2014-2018 Howard Chu, Symas Corp. All Rights Reserved.
 .\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
 .SH NAME
 mdb_dump \- LMDB environment export tool
 .SH SYNOPSIS
 .B mdb_dump
 [\c
 .BR \-V ]
 [\c
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_dump.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_dump.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_dump.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_dump.c
@@ -1,11 +1,11 @@
 /* mdb_dump.c - memory-mapped database dump tool */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_load.1
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_load.1
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_load.1
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_load.1
@@ -1,10 +1,10 @@
 .TH MDB_LOAD 1 "2015/09/30" "LMDB 0.9.17"
-.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copyright 2014-2018 Howard Chu, Symas Corp. All Rights Reserved.
 .\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
 .SH NAME
 mdb_load \- LMDB environment import tool
 .SH SYNOPSIS
 .B mdb_load
 [\c
 .BR \-V ]
 [\c
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_load.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_load.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_load.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_load.c
@@ -1,11 +1,11 @@
 /* mdb_load.c - memory-mapped database load tool */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
@@ -63,16 +63,17 @@ flagbit dbflags[] = {
 	{ MDB_REVERSEDUP, S("reversedup") },
 	{ 0, NULL, 0 }
 };
 
 static void readhdr(void)
 {
 	char *ptr;
 
+	flags = 0;
 	while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) {
 		lineno++;
 		if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) {
 			version=atoi((char *)dbuf.mv_data+STRLENOF("VERSION="));
 			if (version > 3) {
 				fprintf(stderr, "%s: line %" Z "d: unsupported VERSION %d\n",
 					prog, lineno, version);
 				exit(EXIT_FAILURE);
@@ -369,17 +370,16 @@ int main(int argc, char *argv[])
 	}
 
 	kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2;
 	kbuf.mv_data = malloc(kbuf.mv_size);
 
 	while(!Eof) {
 		MDB_val key, data;
 		int batch = 0;
-		flags = 0;
 
 		if (!dohdr) {
 			dohdr = 1;
 		} else if (!(mode & NOHDR))
 			readhdr();
 		
 		rc = mdb_txn_begin(env, NULL, 0, &txn);
 		if (rc) {
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_stat.1
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_stat.1
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_stat.1
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_stat.1
@@ -1,10 +1,10 @@
 .TH MDB_STAT 1 "2015/09/30" "LMDB 0.9.17"
-.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copyright 2012-2018 Howard Chu, Symas Corp. All Rights Reserved.
 .\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
 .SH NAME
 mdb_stat \- LMDB environment status tool
 .SH SYNOPSIS
 .B mdb_stat
 [\c
 .BR \-V ]
 [\c
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_stat.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_stat.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mdb_stat.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mdb_stat.c
@@ -1,11 +1,11 @@
 /* mdb_stat.c - memory-mapped database status tool */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/midl.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/midl.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.c
@@ -1,15 +1,15 @@
 /**	@file midl.c
  *	@brief ldap bdb back-end ID List functions */
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2000-2016 The OpenLDAP Foundation.
- * Portions Copyright 2001-2017 Howard Chu, Symas Corp.
+ * Copyright 2000-2018 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/midl.h
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.h
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/midl.h
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.h
@@ -6,18 +6,18 @@
  *	in this file are unused, just left over from the original.
  *
  *	This file is only used internally in libmdb and its definitions
  *	are not exposed publicly.
  */
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2000-2016 The OpenLDAP Foundation.
- * Portions Copyright 2001-2017 Howard Chu, Symas Corp.
+ * Copyright 2000-2018 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest.c
@@ -1,11 +1,11 @@
 /* mtest.c - memory-mapped database tester/toy */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest2.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest2.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest2.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest2.c
@@ -1,11 +1,11 @@
 /* mtest2.c - memory-mapped database tester/toy */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest3.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest3.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest3.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest3.c
@@ -1,11 +1,11 @@
 /* mtest3.c - memory-mapped database tester/toy */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest4.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest4.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest4.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest4.c
@@ -1,11 +1,11 @@
 /* mtest4.c - memory-mapped database tester/toy */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest5.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest5.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest5.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest5.c
@@ -1,11 +1,11 @@
 /* mtest5.c - memory-mapped database tester/toy */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest6.c
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest6.c
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/mtest6.c
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/mtest6.c
@@ -1,11 +1,11 @@
 /* mtest6.c - memory-mapped database tester/toy */
 /*
- * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2011-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/sample-bdb.txt
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/sample-bdb.txt
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/sample-bdb.txt
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/sample-bdb.txt
@@ -1,14 +1,14 @@
 /* sample-bdb.txt - BerkeleyDB toy/sample
  *
  * Do a line-by-line comparison of this and sample-mdb.txt
  */
 /*
- * Copyright 2012-2017 Howard Chu, Symas Corp.
+ * Copyright 2012-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/sample-mdb.txt
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/sample-mdb.txt
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/sample-mdb.txt
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/sample-mdb.txt
@@ -1,14 +1,14 @@
 /* sample-mdb.txt - MDB toy/sample
  *
  * Do a line-by-line comparison of this and sample-bdb.txt
  */
 /*
- * Copyright 2012-2017 Howard Chu, Symas Corp.
+ * Copyright 2012-2018 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted only as authorized by the OpenLDAP
  * Public License.
  *
  * A copy of this license is available in the file LICENSE in the
  * top-level directory of the distribution or, alternatively, at
rename from third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/tooltag
rename to third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/tooltag
rename from third_party/rust/lmdb-sys/src/constants.rs
rename to third_party/rust/lmdb-rkv-sys/src/constants.rs
rename from third_party/rust/lmdb-sys/src/ffi.rs
rename to third_party/rust/lmdb-rkv-sys/src/ffi.rs
rename from third_party/rust/lmdb-sys/src/lib.rs
rename to third_party/rust/lmdb-rkv-sys/src/lib.rs
--- a/third_party/rust/lmdb-sys/src/lib.rs
+++ b/third_party/rust/lmdb-rkv-sys/src/lib.rs
@@ -1,11 +1,11 @@
 #![allow(non_camel_case_types)]
 #![deny(warnings)]
-#![doc(html_root_url = "https://docs.rs/lmdb-sys/0.8.0")]
+#![doc(html_root_url = "https://docs.rs/lmdb-rkv-sys/0.8.2")]
 
 extern crate libc;
 
 #[cfg(unix)]
 #[allow(non_camel_case_types)]
 pub type mode_t = ::libc::mode_t;
 #[cfg(windows)]
 #[allow(non_camel_case_types)]
--- a/third_party/rust/lmdb-rkv/.cargo-checksum.json
+++ b/third_party/rust/lmdb-rkv/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"fec74a5e98cd6a19d96eb3fcb54504199035eed36b999fe50c874f8e12bbf28d","LICENSE":"db6d163be642e3b568c5fb2104013da632316ecd4e75935df1613af8b0b37933","README.md":"97b61d73ff27afb03bde9ae960f12651093558214851303c8ae82f567abfe992","src/cursor.rs":"1cf47e15c55e3df7db5293f81c7538b0139dd2c4414cdc3ee8799f8659a2af0d","src/database.rs":"003a214f53acd632bc70f2f02d01dcb0bc5bf7e777e1781ef1ff19246f0157d0","src/environment.rs":"be682d90b86aef52d68936d96325d9ce9579389c79ce7b3fc640a79a3836c34b","src/error.rs":"0ea99c8bc1619f3789eff7734341efa7f48fcd8732dc9f3141804e0a802f5d71","src/flags.rs":"40fd3d4d72c8db8f9ecb893420300a3585e2ca4c49073065ec9ebf24fe23c064","src/lib.rs":"1e070c161e33eb07ed3e992f827c4b86e6f682ad0a24442346afb4dbc6ab40dd","src/transaction.rs":"e9e9485aafb19cab4688dc8a476eec033c21c8256717a89080cfb77d2dfe235c"},"package":"aba42e6d930590ce6625566f2d16300add86ef11335eedba0fcf29f9f4cbd8c5"}
\ No newline at end of file
+{"files":{"Cargo.toml":"ba319c56424eed8ac65e6add74a59e9610c2a1e30701801b0ede6313020cd596","LICENSE":"db6d163be642e3b568c5fb2104013da632316ecd4e75935df1613af8b0b37933","README.md":"97b61d73ff27afb03bde9ae960f12651093558214851303c8ae82f567abfe992","azure-pipelines-template.yml":"a75d2421df0feefcb2ea17072c12795e58f9ac6322bacfbea90890fbb5e09cb7","azure-pipelines.yml":"c5a206822c22921bd07ea0a469db734875361457400427991319808cfd13ffef","src/cursor.rs":"f51184cbf015d1aef1a45f0cc1a950524114e1d4aadc50bde7bdb712030a839a","src/database.rs":"003a214f53acd632bc70f2f02d01dcb0bc5bf7e777e1781ef1ff19246f0157d0","src/environment.rs":"774241ef807892b0ebede3392113560563dc8b197c87d97756c47811716a8b4c","src/error.rs":"0ea99c8bc1619f3789eff7734341efa7f48fcd8732dc9f3141804e0a802f5d71","src/flags.rs":"40fd3d4d72c8db8f9ecb893420300a3585e2ca4c49073065ec9ebf24fe23c064","src/lib.rs":"e8c93f6501c58f2d325998730e7991c156a8f53459130181005569ede9d2d871","src/transaction.rs":"09ac6501e3dd1a714c635acb46625c4a4d96d9296cd30bcda31d50224e27fd05"},"package":"1452294309db7977dc75e1e8135a8c654d9e52e04ff0c0bd06c880897a91defd"}
\ No newline at end of file
--- a/third_party/rust/lmdb-rkv/Cargo.toml
+++ b/third_party/rust/lmdb-rkv/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "lmdb-rkv"
-version = "0.9.0"
+version = "0.11.2"
 authors = ["Dan Burkert <dan@danburkert.com>"]
 description = "Idiomatic and safe LMDB wrapper."
 documentation = "https://docs.rs/lmdb-rkv"
 readme = "README.md"
 keywords = ["LMDB", "database", "storage-engine", "bindings", "library"]
 categories = ["database"]
 license = "Apache-2.0"
 repository = "https://github.com/mozilla/lmdb-rs.git"
@@ -25,18 +25,18 @@ repository = "https://github.com/mozilla
 [lib]
 name = "lmdb"
 [dependencies.bitflags]
 version = "1"
 
 [dependencies.libc]
 version = "0.2"
 
-[dependencies.lmdb-sys]
-version = "0.8.0"
+[dependencies.lmdb-rkv-sys]
+version = "0.8.2"
 [dev-dependencies.byteorder]
 version = "1.0"
 
 [dev-dependencies.rand]
 version = "0.4"
 
 [dev-dependencies.tempdir]
 version = "0.3"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/lmdb-rkv/azure-pipelines-template.yml
@@ -0,0 +1,49 @@
+jobs:
+- job: ${{ parameters.name }}
+  pool:
+    vmImage: ${{ parameters.vmImage }}
+  strategy:
+    matrix:
+      stable:
+        rustup_toolchain: stable
+      beta:
+        rustup_toolchain: beta
+      nightly:
+        rustup_toolchain: nightly
+  steps:
+  # Linux and macOS.
+  - ${{ if ne(parameters.name, 'Windows') }}:
+    - script: |
+        curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
+        echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
+      displayName: Install rust
+  # Windows.
+  - ${{ if eq(parameters.name, 'Windows') }}:
+    - script: |
+        curl -sSf -o rustup-init.exe https://win.rustup.rs
+        rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
+        set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+        echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
+      displayName: Install rust (windows)
+  # All platforms.
+  - script: |
+        rustc -Vv
+        cargo -V
+    displayName: Query rust and cargo versions
+  - script: cargo build
+    displayName: Build
+  # Linux and macOS w/nightly toolchain.
+  # Ideally we'd only run the script for the nightly toolchain, but I can't
+  # figure out how to determine that within the Azure Pipelines conditional.
+  - ${{ if ne(parameters.name, 'Windows') }}:
+    - script: |
+        if [ "$RUSTUP_TOOLCHAIN" = 'nightly' ]
+          then cargo test
+        fi
+      displayName: Test
+  # Windows w/nightly toolchain.
+  # Ideally we'd only run the script for the nightly toolchain, but I can't
+  # figure out how to determine that within the Azure Pipelines conditional.
+  - ${{ if eq(parameters.name, 'Windows') }}:
+    - script: if "%RUSTUP_TOOLCHAIN%" == "nightly" cargo test
+      displayName: Test
new file mode 100644
--- /dev/null
+++ b/third_party/rust/lmdb-rkv/azure-pipelines.yml
@@ -0,0 +1,15 @@
+jobs:
+- template: azure-pipelines-template.yml
+  parameters:
+    name: macOS
+    vmImage: macOS-10.13
+
+- template: azure-pipelines-template.yml
+  parameters:
+    name: Linux
+    vmImage: ubuntu-16.04
+
+- template: azure-pipelines-template.yml
+  parameters:
+    name: Windows
+    vmImage: vs2017-win2016
--- a/third_party/rust/lmdb-rkv/src/cursor.rs
+++ b/third_party/rust/lmdb-rkv/src/cursor.rs
@@ -55,17 +55,17 @@ pub trait Cursor<'txn> {
     /// Iterate over database items starting from the given key.
     ///
     /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the
     /// duplicate data items of each key will be returned before moving on to
     /// the next key.
     fn iter_from<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> {
         match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
             Ok(_) | Err(Error::NotFound) => (),
-            Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
+            Err(error) => return Iter::Err(error),
         };
         Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
     }
 
     /// Iterate over duplicate database items. The iterator will begin with the
     /// item next after the cursor, and continue until the end of the database.
     /// Each item will be returned as an iterator of its duplicates.
     fn iter_dup(&mut self) -> IterDup<'txn> {
@@ -75,29 +75,29 @@ pub trait Cursor<'txn> {
     /// Iterate over duplicate database items starting from the beginning of the
     /// database. Each item will be returned as an iterator of its duplicates.
     fn iter_dup_start(&mut self) -> IterDup<'txn> {
         IterDup::new(self.cursor(), ffi::MDB_FIRST)
     }
 
     /// Iterate over duplicate items in the database starting from the given
     /// key. Each item will be returned as an iterator of its duplicates.
-    fn iter_dup_from<K>(&mut self, key: &K) -> IterDup<'txn> where K: AsRef<[u8]> {
+    fn iter_dup_from<K>(&mut self, key: K) -> IterDup<'txn> where K: AsRef<[u8]> {
         match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
             Ok(_) | Err(Error::NotFound) => (),
-            Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
+            Err(error) => return IterDup::Err(error),
         };
         IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
     }
 
     /// Iterate over the duplicates of the item in the database with the given key.
-    fn iter_dup_of<K>(&mut self, key: &K) -> Iter<'txn> where K: AsRef<[u8]> {
+    fn iter_dup_of<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> {
         match self.get(Some(key.as_ref()), None, ffi::MDB_SET) {
             Ok(_) | Err(Error::NotFound) => (),
-            Err(error) => panic!("mdb_cursor_get returned an unexpected error: {}", error),
+            Err(error) => return Iter::Err(error),
         };
         Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)
     }
 }
 
 /// A read-only cursor for navigating the items within a database.
 pub struct RoCursor<'txn> {
     cursor: *mut ffi::MDB_cursor,
@@ -209,98 +209,146 @@ unsafe fn slice_to_val(slice: Option<&[u
                            mv_data: ptr::null_mut() },
     }
 }
 
 unsafe fn val_to_slice<'a>(val: ffi::MDB_val) -> &'a [u8] {
     slice::from_raw_parts(val.mv_data as *const u8, val.mv_size as usize)
 }
 
-/// An iterator over the values in an LMDB database.
-pub struct Iter<'txn> {
-    cursor: *mut ffi::MDB_cursor,
-    op: c_uint,
-    next_op: c_uint,
-    _marker: PhantomData<fn(&'txn ())>,
+/// An iterator over the key/value pairs in an LMDB database.
+pub enum Iter<'txn> {
+    /// An iterator that returns an error on every call to Iter.next().
+    /// Cursor.iter*() creates an Iter of this type when LMDB returns an error
+    /// on retrieval of a cursor.  Using this variant instead of returning
+    /// an error makes Cursor.iter()* methods infallible, so consumers only
+    /// need to check the result of Iter.next().
+    Err(Error),
+
+    /// An iterator that returns an Item on calls to Iter.next().
+    /// The Item is a Result<(&'txn [u8], &'txn [u8])>, so this variant
+    /// might still return an error, if retrieval of the key/value pair
+    /// fails for some reason.
+    Ok {
+        /// The LMDB cursor with which to iterate.
+        cursor: *mut ffi::MDB_cursor,
+
+        /// The first operation to perform when the consumer calls Iter.next().
+        op: c_uint,
+
+        /// The next and subsequent operations to perform.
+        next_op: c_uint,
+
+        /// A marker to ensure the iterator doesn't outlive the transaction.
+        _marker: PhantomData<fn(&'txn ())>,
+    },
 }
 
 impl <'txn> Iter<'txn> {
 
     /// Creates a new iterator backed by the given cursor.
     fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
-        Iter { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData }
+        Iter::Ok { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData }
     }
 }
 
 impl <'txn> fmt::Debug for Iter<'txn> {
     fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
         f.debug_struct("Iter").finish()
     }
 }
 
 impl <'txn> Iterator for Iter<'txn> {
 
-    type Item = (&'txn [u8], &'txn [u8]);
+    type Item = Result<(&'txn [u8], &'txn [u8])>;
 
-    fn next(&mut self) -> Option<(&'txn [u8], &'txn [u8])> {
-        let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
-        let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
-        let op = mem::replace(&mut self.op, self.next_op);
-        unsafe {
-            match ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op) {
-                ffi::MDB_SUCCESS => Some((val_to_slice(key), val_to_slice(data))),
-                // EINVAL can occur when the cursor was previously seeked to a non-existent value,
-                // e.g. iter_from with a key greater than all values in the database.
-                ffi::MDB_NOTFOUND | EINVAL => None,
-                error => panic!("mdb_cursor_get returned an unexpected error: {}", error),
-            }
+    fn next(&mut self) -> Option<Result<(&'txn [u8], &'txn [u8])>> {
+        match self {
+            &mut Iter::Ok { cursor, ref mut op, next_op, _marker } => {
+                let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
+                let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
+                let op = mem::replace(op, next_op);
+                unsafe {
+                    match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) {
+                        ffi::MDB_SUCCESS => Some(Ok((val_to_slice(key), val_to_slice(data)))),
+                        // EINVAL can occur when the cursor was previously seeked to a non-existent value,
+                        // e.g. iter_from with a key greater than all values in the database.
+                        ffi::MDB_NOTFOUND | EINVAL => None,
+                        error => Some(Err(Error::from_err_code(error))),
+                    }
+                }
+            },
+            &mut Iter::Err(err) => Some(Err(err)),
         }
     }
 }
 
 /// An iterator over the keys and duplicate values in an LMDB database.
 ///
 /// The yielded items of the iterator are themselves iterators over the duplicate values for a
 /// specific key.
-pub struct IterDup<'txn> {
-    cursor: *mut ffi::MDB_cursor,
-    op: c_uint,
-    _marker: PhantomData<fn(&'txn ())>,
+pub enum IterDup<'txn> {
+    /// An iterator that returns an error on every call to Iter.next().
+    /// Cursor.iter*() creates an Iter of this type when LMDB returns an error
+    /// on retrieval of a cursor.  Using this variant instead of returning
+    /// an error makes Cursor.iter()* methods infallible, so consumers only
+    /// need to check the result of Iter.next().
+    Err(Error),
+
+    /// An iterator that returns an Item on calls to Iter.next().
+    /// The Item is a Result<(&'txn [u8], &'txn [u8])>, so this variant
+    /// might still return an error, if retrieval of the key/value pair
+    /// fails for some reason.
+    Ok {
+        /// The LMDB cursor with which to iterate.
+        cursor: *mut ffi::MDB_cursor,
+
+        /// The first operation to perform when the consumer calls Iter.next().
+        op: c_uint,
+
+        /// A marker to ensure the iterator doesn't outlive the transaction.
+        _marker: PhantomData<fn(&'txn ())>,
+    },
 }
 
 impl <'txn> IterDup<'txn> {
 
     /// Creates a new iterator backed by the given cursor.
     fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
-        IterDup { cursor: cursor, op: op, _marker: PhantomData }
+        IterDup::Ok { cursor: cursor, op: op, _marker: PhantomData }
     }
 }
 
 impl <'txn> fmt::Debug for IterDup<'txn> {
     fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
         f.debug_struct("IterDup").finish()
     }
 }
 
 impl <'txn> Iterator for IterDup<'txn> {
 
     type Item = Iter<'txn>;
 
     fn next(&mut self) -> Option<Iter<'txn>> {
-        let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
-        let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
-        let op = mem::replace(&mut self.op, ffi::MDB_NEXT_NODUP);
-        let err_code = unsafe {
-            ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op)
-        };
+        match self {
+            &mut IterDup::Ok { cursor, ref mut op, _marker } => {
+                let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
+                let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
+                let op = mem::replace(op, ffi::MDB_NEXT_NODUP);
+                let err_code = unsafe {
+                    ffi::mdb_cursor_get(cursor, &mut key, &mut data, op)
+                };
 
-        if err_code == ffi::MDB_SUCCESS {
-            Some(Iter::new(self.cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
-        } else {
-            None
+                if err_code == ffi::MDB_SUCCESS {
+                    Some(Iter::new(cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
+                } else {
+                    None
+                }
+            },
+            &mut IterDup::Err(err) => Some(Iter::Err(err)),
         }
     }
 }
 
 #[cfg(test)]
 mod test {
 
     use std::ptr;
@@ -428,32 +476,40 @@ mod test {
             for &(ref key, ref data) in &items {
                 txn.put(db, key, data, WriteFlags::empty()).unwrap();
             }
             txn.commit().unwrap();
         }
 
         let txn = env.begin_ro_txn().unwrap();
         let mut cursor = txn.open_ro_cursor(db).unwrap();
-        assert_eq!(items, cursor.iter().collect::<Vec<_>>());
+
+        // Because Result implements FromIterator, we can collect the iterator
+        // of items of type Result<_, E> into a Result<Vec<_, E>> by specifying
+        // the collection type via the turbofish syntax.
+        assert_eq!(items, cursor.iter().collect::<Result<Vec<_>>>().unwrap());
+
+        // Alternately, we can collect it into an appropriately typed variable.
+        let retr: Result<Vec<_>> = cursor.iter_start().collect();
+        assert_eq!(items, retr.unwrap());
 
         cursor.get(Some(b"key2"), None, MDB_SET).unwrap();
         assert_eq!(items.clone().into_iter().skip(2).collect::<Vec<_>>(),
-                   cursor.iter().collect::<Vec<_>>());
+                   cursor.iter().collect::<Result<Vec<_>>>().unwrap());
 
-        assert_eq!(items, cursor.iter_start().collect::<Vec<_>>());
+        assert_eq!(items, cursor.iter_start().collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(items.clone().into_iter().skip(1).collect::<Vec<_>>(),
-                   cursor.iter_from(b"key2").collect::<Vec<_>>());
+                   cursor.iter_from(b"key2").collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<_>>(),
-                   cursor.iter_from(b"key4").collect::<Vec<_>>());
+                   cursor.iter_from(b"key4").collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_from(b"key6").collect::<Vec<_>>());
+                   cursor.iter_from(b"key6").collect::<Result<Vec<_>>>().unwrap());
     }
 
     #[test]
     fn test_iter_empty_database() {
         let dir = TempDir::new("test").unwrap();
         let env = Environment::new().open(dir.path()).unwrap();
         let db = env.open_db(None).unwrap();
         let txn = env.begin_ro_txn().unwrap();
@@ -505,39 +561,39 @@ mod test {
             for &(ref key, ref data) in &items {
                 txn.put(db, key, data, WriteFlags::empty()).unwrap();
             }
             txn.commit().unwrap();
         }
 
         let txn = env.begin_ro_txn().unwrap();
         let mut cursor = txn.open_ro_cursor(db).unwrap();
-        assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::<Vec<_>>());
+        assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
 
         cursor.get(Some(b"b"), None, MDB_SET).unwrap();
         assert_eq!(items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_dup().flat_map(|x| x).collect::<Vec<_>>());
+                   cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(items,
-                   cursor.iter_dup_start().flat_map(|x| x).collect::<Vec<(&[u8], &[u8])>>());
+                   cursor.iter_dup_start().flat_map(|x| x).collect::<Result<Vec<(&[u8], &[u8])>>>().unwrap());
 
         assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_dup_from(b"b").flat_map(|x| x).collect::<Vec<_>>());
+                   cursor.iter_dup_from(b"b").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::<Vec<_>>());
+                   cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(items.clone().into_iter().skip(9).collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_dup_from(b"d").flat_map(|x| x).collect::<Vec<_>>());
+                   cursor.iter_dup_from(b"d").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Vec<_>>());
+                   cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(),
-                   cursor.iter_dup_of(b"b").collect::<Vec<_>>());
+                   cursor.iter_dup_of(b"b").collect::<Result<Vec<_>>>().unwrap());
 
         assert_eq!(0, cursor.iter_dup_of(b"foo").count());
     }
 
     #[test]
     fn test_put_del() {
         let dir = TempDir::new("test").unwrap();
         let env = Environment::new().open(dir.path()).unwrap();
@@ -566,21 +622,37 @@ mod test {
         let db = env.open_db(None).unwrap();
         let txn = env.begin_ro_txn().unwrap();
 
         b.iter(|| {
             let mut cursor = txn.open_ro_cursor(db).unwrap();
             let mut i = 0;
             let mut count = 0u32;
 
-            for (key, data) in cursor.iter() {
+            for (key, data) in cursor.iter().map(Result::unwrap) {
+                i = i + key.len() + data.len();
+                count = count + 1;
+            }
+            for (key, data) in cursor.iter().filter_map(Result::ok) {
                 i = i + key.len() + data.len();
                 count = count + 1;
             }
 
+            fn iterate<'a>(cursor: &mut RoCursor) -> Result<()> {
+                let mut i = 0;
+                let mut count = 0u32;
+                for result in cursor.iter() {
+                    let (key, data) = result?;
+                    i = i + key.len() + data.len();
+                    count = count + 1;
+                }
+                Ok(())
+            }
+            iterate(&mut cursor).unwrap();
+
             black_box(i);
             assert_eq!(count, n);
         });
     }
 
     /// Benchmark of cursor sequential read performance.
     #[bench]
     fn bench_get_seq_cursor(b: &mut Bencher) {
--- a/third_party/rust/lmdb-rkv/src/environment.rs
+++ b/third_party/rust/lmdb-rkv/src/environment.rs
@@ -311,17 +311,17 @@ impl EnvironmentBuilder {
     pub fn set_max_dbs(&mut self, max_readers: c_uint) -> &mut EnvironmentBuilder {
         self.max_dbs = Some(max_readers);
         self
     }
 
     /// Sets the size of the memory map to use for the environment.
     ///
     /// The size should be a multiple of the OS page size. The default is
-    /// 10485760 bytes. The size of the memory map is also the maximum size
+    /// 1048576 bytes. The size of the memory map is also the maximum size
     /// of the database. The value should be chosen as large as possible,
     /// to accommodate future growth of the database. It may be increased at
     /// later times.
     ///
     /// Any attempt to set a size smaller than the space already consumed
     /// by the environment will be silently changed to the current size of the used space.
     pub fn set_map_size(&mut self, map_size: size_t) -> &mut EnvironmentBuilder {
         self.map_size = Some(map_size);
--- a/third_party/rust/lmdb-rkv/src/lib.rs
+++ b/third_party/rust/lmdb-rkv/src/lib.rs
@@ -1,17 +1,17 @@
 //! Idiomatic and safe APIs for interacting with the
 //! [Lightning Memory-mapped Database (LMDB)](https://symas.com/lmdb).
 
 #![cfg_attr(test, feature(test))]
 #![deny(missing_docs)]
-#![doc(html_root_url = "https://docs.rs/lmdb-rkv/0.9.0")]
+#![doc(html_root_url = "https://docs.rs/lmdb-rkv/0.11.2")]
 
 extern crate libc;
-extern crate lmdb_sys as ffi;
+extern crate lmdb_rkv_sys as ffi;
 
 #[cfg(test)] extern crate rand;
 #[cfg(test)] extern crate tempdir;
 #[cfg(test)] extern crate test;
 #[macro_use] extern crate bitflags;
 
 pub use cursor::{
     Cursor,
--- a/third_party/rust/lmdb-rkv/src/transaction.rs
+++ b/third_party/rust/lmdb-rkv/src/transaction.rs
@@ -327,22 +327,32 @@ impl <'env> RwTransaction<'env> {
            -> Result<()>
     where K: AsRef<[u8]> {
         let key = key.as_ref();
         let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t,
                                                        mv_data: key.as_ptr() as *mut c_void };
         let data_val: Option<ffi::MDB_val> =
             data.map(|data| ffi::MDB_val { mv_size: data.len() as size_t,
                                            mv_data: data.as_ptr() as *mut c_void });
-        unsafe {
-            lmdb_result(ffi::mdb_del(self.txn(),
-                                     database.dbi(),
-                                     &mut key_val,
-                                     data_val.map(|mut data_val| &mut data_val as *mut _)
-                                             .unwrap_or(ptr::null_mut())))
+
+        if let Some(mut d) = data_val {
+            unsafe {
+                lmdb_result(ffi::mdb_del(self.txn(),
+                                         database.dbi(),
+                                         &mut key_val,
+                                         &mut d))
+
+            }
+        } else {
+            unsafe {
+                lmdb_result(ffi::mdb_del(self.txn(),
+                                         database.dbi(),
+                                         &mut key_val,
+                                         ptr::null_mut()))
+            }
         }
     }
 
     /// Empties the given database. All items will be removed.
     pub fn clear_db(&mut self, db: Database) -> Result<()> {
         unsafe { lmdb_result(ffi::mdb_drop(self.txn(), db.dbi(), 0)) }
     }
 
@@ -387,16 +397,17 @@ mod test {
     use tempdir::TempDir;
 
     use environment::*;
     use error::*;
     use ffi::*;
     use flags::*;
     use super::*;
     use test_utils::*;
+    use cursor::Cursor;
 
     #[test]
     fn test_put_get_del() {
         let dir = TempDir::new("test").unwrap();
         let env = Environment::new().open(dir.path()).unwrap();
         let db = env.open_db(None).unwrap();
 
         let mut txn = env.begin_rw_txn().unwrap();
@@ -411,16 +422,63 @@ mod test {
         assert_eq!(b"val3", txn.get(db, b"key3").unwrap());
         assert_eq!(txn.get(db, b"key"), Err(Error::NotFound));
 
         txn.del(db, b"key1", None).unwrap();
         assert_eq!(txn.get(db, b"key1"), Err(Error::NotFound));
     }
 
     #[test]
+    fn test_put_get_del_multi() {
+        let dir = TempDir::new("test").unwrap();
+        let env = Environment::new().open(dir.path()).unwrap();
+        let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
+
+        let mut txn = env.begin_rw_txn().unwrap();
+        txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key2", b"val1", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key3", b"val1", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key3", b"val2", WriteFlags::empty()).unwrap();
+        txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
+        txn.commit().unwrap();
+
+        let txn = env.begin_rw_txn().unwrap();
+        {
+            let mut cur = txn.open_ro_cursor(db).unwrap();
+            let iter = cur.iter_dup_of(b"key1");
+            let vals = iter.map(|x| x.unwrap()).map(|(_,x)| x).collect::<Vec<_>>();
+            assert_eq!(vals, vec![b"val1", b"val2", b"val3"]);
+
+        }
+        txn.commit().unwrap();
+
+        let mut txn = env.begin_rw_txn().unwrap();
+        txn.del(db, b"key1", Some(b"val2")).unwrap();
+        txn.del(db, b"key2", None).unwrap();
+        txn.commit().unwrap();
+
+        let txn = env.begin_rw_txn().unwrap();
+        {
+            let mut cur = txn.open_ro_cursor(db).unwrap();
+            let iter = cur.iter_dup_of(b"key1");
+            let vals = iter.map(|x| x.unwrap()).map(|(_,x)| x).collect::<Vec<_>>();
+            assert_eq!(vals, vec![b"val1", b"val3"]);
+
+            let iter = cur.iter_dup_of(b"key2");
+            assert_eq!(0, iter.count());
+        }
+        txn.commit().unwrap();
+    }
+
+
+    #[test]
     fn test_reserve() {
         let dir = TempDir::new("test").unwrap();
         let env = Environment::new().open(dir.path()).unwrap();
         let db = env.open_db(None).unwrap();
 
         let mut txn = env.begin_rw_txn().unwrap();
         {
             let mut writer = txn.reserve(db, b"key1", 4, WriteFlags::empty()).unwrap();
deleted file mode 100644
--- a/third_party/rust/lmdb-sys/.cargo-checksum.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{"files":{"Cargo.toml":"6d08d8713cae81e984bb50cbeb309c7746f4eb6b747a8e2affdca9c30230d353","build.rs":"4204dd0891e2eca08cb7cbcccfb36d1bdac137651c383f4a36368478fd836402","lmdb/libraries/liblmdb/.gitignore":"49e1a82819dab072b1c80d4570f817d6e2c6625d574df871f7a065ab44944727","lmdb/libraries/liblmdb/CHANGES":"d58e7fbdac22b7001c141df2f48b9d192770de52c9b4a0348552dcd4056ebbf7","lmdb/libraries/liblmdb/COPYRIGHT":"ce2436e2ab324485500b6fec5764b788ba082ac902ed00290f527d0170d8c90e","lmdb/libraries/liblmdb/Doxyfile":"5545f6b049040ce58e6d1a603eaea6b7fb8ae92459f2ab8d3bcbacabcce1014d","lmdb/libraries/liblmdb/LICENSE":"310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569","lmdb/libraries/liblmdb/Makefile":"60b5f574e6642602f692a95956da61c588a265ad50b8059960c230b9e6aaf4fd","lmdb/libraries/liblmdb/intro.doc":"c7c2d970e7c277dc1f45bb3824c054fb73cf8d1666aa826e83c6a9a6a378839d","lmdb/libraries/liblmdb/lmdb.h":"c06ce42284b7544657e7b2d6b1f34cedd85234c193e673167b5d4b86372142ca","lmdb/libraries/liblmdb/mdb.c":"75b99ad8ef4ef747fb6ba93cc54b2d20cd91bef9121450c449cb2dbd77bf6d31","lmdb/libraries/liblmdb/mdb_copy.1":"c01f113a295d3c76b60f432896f24862cb0d2dadb14e26d72c6de9b55a254e2f","lmdb/libraries/liblmdb/mdb_copy.c":"b75a4d9b18aea89b8910bff44938244587a1a5863d1d609134298b1b5f019bfe","lmdb/libraries/liblmdb/mdb_dump.1":"adf51a57167bc6a64e0e6635e28ad2175924754f25eb1e9d89278b543c5e78d3","lmdb/libraries/liblmdb/mdb_dump.c":"7a67fa80539cb976cedd2bf7c56082366c33e07c9eac409e47efabaf4e31b62f","lmdb/libraries/liblmdb/mdb_load.1":"c1f7369d12b84fc92908256d3c1e422fa05576bde378fc84aca272d5aa2f4102","lmdb/libraries/liblmdb/mdb_load.c":"136169ce000cf254149ac00a0d9d4b12e9548649e7cdc9f6d6bd0943c16c11ea","lmdb/libraries/liblmdb/mdb_stat.1":"9c531fa57f1a528fa4bc4da4351b2332bff2e34bbd6f76dd0c1bbd915888cbfd","lmdb/libraries/liblmdb/mdb_stat.c":"235b872ae85aff627dbf27ae495c66cf3ed80eeca2188fe90d27d0486c03d0fa","lmdb/libraries/liblmdb/midl.c":"4877d6421f114a18a7074076b5b56c68567c0ba1a8328018af788cf5bb5c4f87","lmdb/libraries/liblmdb/midl.h":"853fc3115d2b827d6b53afcb03d39e0dcab6be108108955e549990ee5ece5c8a","lmdb/libraries/liblmdb/mtest.c":"4fd58e1ab4e445a73a6c655275f2c84523c128d34afa958a98b128c1d5f7ec24","lmdb/libraries/liblmdb/mtest2.c":"4b5711048ecbc255e3393c2fab589630b78fc3fc423cc98281629c7e45e78b4b","lmdb/libraries/liblmdb/mtest3.c":"b8e895e093a9d5bacbfde677d8f8e0aca9f0b9d8c2f4392bb47ff227b9c6a08d","lmdb/libraries/liblmdb/mtest4.c":"6e1fd62ca1036795dbadd1306fae4e48c34f00576805c0ac1dd73114ec58f85c","lmdb/libraries/liblmdb/mtest5.c":"24906ec930c259be3c2af5250fe1cb06dbb0f40ce3bad3c9f4991f681642e1e9","lmdb/libraries/liblmdb/mtest6.c":"f5c40dc672e5946874fe8a6a2d10fe8da2f509ffe48c73bdd4199b2d6756031c","lmdb/libraries/liblmdb/sample-bdb.txt":"7692f24c5f909328856008ed90e48cf224de1eb466d1f7623ffb7f32148eb3d9","lmdb/libraries/liblmdb/sample-mdb.txt":"40902b2560d72d348d19a23711244c59e3d684871a4262071312cc5a67e5ecae","lmdb/libraries/liblmdb/tooltag":"4734c6dc1fa7aec8c2e9646bd04bc5218ef6a03ad83a3b18de2ac4069eb94120","src/constants.rs":"af67740b5acccdc71b2267ec051bb60e5433c4f0313fe16dc0627376a52dcdff","src/ffi.rs":"caa9bbfb3868a7a9e9ad822d775e60ffa8c8c2f2450ac4ed403a93ddb7547899","src/lib.rs":"93504d2712ee68182bb59d5c6ad440b42892c221ecf014e77c325f2bb5cdddf1"},"package":null}
\ No newline at end of file
deleted file mode 100644
--- a/third_party/rust/lmdb-sys/Cargo.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-[package]
-
-name = "lmdb-sys"
-# NB: When modifying, also modify html_root_url in lib.rs
-version = "0.8.0"
-authors = ["Dan Burkert <dan@danburkert.com>"]
-license = "Apache-2.0"
-
-description = "Rust bindings for liblmdb."
-repository = "https://github.com/danburkert/lmdb-rs.git"
-readme = "../README.md"
-documentation = "https://docs.rs/lmdb-sys"
-keywords = ["LMDB", "database", "storage-engine", "bindings", "library"]
-categories = ["database", "external-ffi-bindings"]
-
-build = "build.rs"
-
-[dependencies]
-libc = "0.2"
-
-[build-dependencies]
-pkg-config = "0.3"
-cc = "1"
deleted file mode 100644
--- a/third_party/rust/lmdb-sys/lmdb/libraries/liblmdb/.gitignore
+++ /dev/null
@@ -1,23 +0,0 @@
-mtest
-mtest[23456]
-testdb
-mdb_copy
-mdb_stat
-mdb_dump
-mdb_load
-*.lo
-*.[ao]
-*.so
-*.exe
-*[~#]
-*.bak
-*.orig
-*.rej
-*.gcov
-*.gcda
-*.gcno
-core
-core.*
-valgrind.*
-man/
-html/
--- a/third_party/rust/rkv/.cargo-checksum.json
+++ b/third_party/rust/rkv/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"a80cc710a906ed055c8d76679319d04e1825cbe95309cde92808a068c15bdca0","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"9dc24375b49fef42f35dec42e316e21827d7337622f9e7cf36243cd28808797a","examples/README.md":"143767fc145bf167ce269a65138cb3f7086cb715b8bc4f73626da82966e646f4","examples/iterator.rs":"a47bf9e90828e8ae3a1e5146f87efb5c48c7dbbd6e570e4eed4d6f07c8a2583d","examples/simple-store.rs":"f98feda8ec74f4dc6e06dd43d7708d42dc201a773673e10332d257251d536c41","run-all-examples.sh":"7f9d11d01017f77e1c9d26e3e82dfca8c6930deaec85e864458e33a7fa267de0","src/env.rs":"c501be4ea8e38b0127bf6a4706eae48995066ae0be057c08f36384233968455c","src/error.rs":"495112ac06104c4f83e033604a6121cb2e12140cf6450d38d8a7c8d9f5fea874","src/integer.rs":"3cf748279e4b1dd66c35c7e7989c1d3721d067ace097536756d2903683199680","src/lib.rs":"10d9e9fb91421f19667ee5869c64e26b87d2959813758bc081ce69ee0cb351e4","src/manager.rs":"621cbb3eebcb21fa85368122ba0fa8c721dd399334ca66813b795b396899ee81","src/readwrite.rs":"239115092dca693cea756e468879904fccaa7724b7df384eb2ec302ceda8881f","src/value.rs":"30e9784bb98110585fd1f06f263931a305a33bc457594006442c3352eb88f790","tests/manager.rs":"ebc8a9d6e55b1bc0c0699341141de2ce13e1306386ce5677a0eb4eb749f21d54"},"package":"79504d734e64f3d9391fbcaf15ca247421da461b95b9cdebd9af5679a4cfd8c8"}
\ No newline at end of file
+{"files":{"Cargo.toml":"a8e9c69b953f21a7ff7d1b7dec780f9adcde7f5537b7db4e86db45ff382e9132","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"9dc24375b49fef42f35dec42e316e21827d7337622f9e7cf36243cd28808797a","examples/README.md":"143767fc145bf167ce269a65138cb3f7086cb715b8bc4f73626da82966e646f4","examples/iterator.rs":"ddc3997e394a30ad82d78d2675a48c4617353f88b89bb9a3df5a3804d59b8ef9","examples/simple-store.rs":"9cec5f5a1277edf395775c22a29a27b1d9907ca693a3faa6cbd8e0f0bbff4347","run-all-examples.sh":"7f9d11d01017f77e1c9d26e3e82dfca8c6930deaec85e864458e33a7fa267de0","src/env.rs":"e71264a3ee9d5e87948f56a098099b1394935faa5d77dfcc75aff2c7445634d7","src/error.rs":"46632b8fcb1070a1860247e09a59d39772079ebfba5d3d1bbee03d08e1252275","src/lib.rs":"365cd108bec0e22e8aa010b738a7db2f0da4c6e4cbf1284a1e8ad7e2f1f05736","src/manager.rs":"f06b14ee64f2e58d890a3b37677790b707a02d328242c1af0ce3c74e9028edd8","src/readwrite.rs":"5d5dd64c9b36b7f75b69771e6909c6d48f109ee3725b357f6a9099ddb853e978","src/store.rs":"409d13b1ea0d1254dae947ecbce50e741fb71c3ca118a78803b734336dce6a8f","src/store/integer.rs":"a302c7fb70397b7dca6c116828a309d16c9bc664abe029342d8ebdd730d8b457","src/store/integermulti.rs":"f2c8f9c70d1615757ccb0a56a9642ad6769236fd4c406767f5a71fa84eeeaacf","src/store/multi.rs":"9456f5ff3cec3bf2fc27660b18483e1f0752b5f5f6279b4cfcd1898e236188cb","src/store/single.rs":"09f594b7150cbdad4b8a5dc208d4b0ce4962139b8c856276264dd24c98ac92a4","src/value.rs":"660a8e2fad3d13c3073d15f83f28e16662d9f1a682f6673ee1b885c3c28d44dd","tests/integer-store.rs":"f7e06c71b0dead2323c7c61fc8bcbffbdd3a4796eebf6138db9cce3dbba716a3","tests/manager.rs":"97ec61145dc227f4f5fbcb6449c096bbe5b9a09db4e61ff4491c0443fe9adf26","tests/multi-integer-store.rs":"83295b0135c502321304aa06b05d5a9eeab41b1438ed7ddf2cb1a3613dfef4d9"},"package":"967f320b7357a64909224619c90c1832f4532c3f19ec24350860bd64c2c7e272"}
\ No newline at end of file
--- a/third_party/rust/rkv/Cargo.toml
+++ b/third_party/rust/rkv/Cargo.toml
@@ -6,18 +6,19 @@
 # to registry (e.g. crates.io) dependencies
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
+edition = "2018"
 name = "rkv"
-version = "0.7.0"
+version = "0.9.2"
 authors = ["Richard Newman <rnewman@twinql.com>", "Nan Jiang <najiang@mozilla.com>", "Myk Melez <myk@mykzilla.org>"]
 description = "a simple, humane, typed Rust interface to LMDB"
 homepage = "https://github.com/mozilla/rkv"
 readme = "README.md"
 keywords = ["lmdb", "database", "storage"]
 categories = ["database"]
 license = "Apache-2.0"
 repository = "https://github.com/mozilla/rkv"
@@ -28,27 +29,30 @@ version = "0.3"
 version = "1.0"
 
 [dependencies.failure]
 version = "0.1.2"
 features = ["derive"]
 default_features = false
 
 [dependencies.lazy_static]
-version = "1.0"
+version = "1.0.2"
 
 [dependencies.lmdb-rkv]
-version = "0.9"
+version = "0.11.2"
 
 [dependencies.ordered-float]
 version = "1.0"
 
 [dependencies.serde]
 version = "1.0"
 
+[dependencies.serde_derive]
+version = "1.0"
+
 [dependencies.url]
 version = "1.7.0"
 
 [dependencies.uuid]
 version = "0.7"
 [dev-dependencies.byteorder]
 version = "1"
 
--- a/third_party/rust/rkv/examples/iterator.rs
+++ b/third_party/rust/rkv/examples/iterator.rs
@@ -2,76 +2,74 @@
 // http://creativecommons.org/publicdomain/zero/1.0/
 
 //! A demo that showcases the basic usage of iterators in rkv.
 //!
 //! You can test this out by running:
 //!
 //!     cargo run --example iterator
 
-extern crate rkv;
-extern crate tempfile;
-
 use rkv::{
     Manager,
     Rkv,
-    Store,
+    SingleStore,
     StoreError,
+    StoreOptions,
     Value,
 };
 use tempfile::Builder;
 
 use std::fs;
 use std::str;
 
 fn main() {
     let root = Builder::new().prefix("iterator").tempdir().unwrap();
     fs::create_dir_all(root.path()).unwrap();
     let p = root.path();
 
     let created_arc = Manager::singleton().write().unwrap().get_or_create(p, Rkv::new).unwrap();
     let k = created_arc.read().unwrap();
-    let store = k.open_or_create("store").unwrap();
+    let store = k.open_single("store", StoreOptions::create()).unwrap();
 
     populate_store(&k, store).unwrap();
 
     let reader = k.read().unwrap();
 
     println!("Iterating from the beginning...");
     // Reader::iter_start() iterates from the first item in the store, and
     // returns the (key, value) tuples in order.
-    let mut iter = reader.iter_start(store).unwrap();
-    while let Some((country, city)) = iter.next() {
+    let mut iter = store.iter_start(&reader).unwrap();
+    while let Some(Ok((country, city))) = iter.next() {
         println!("{}, {:?}", str::from_utf8(country).unwrap(), city);
     }
 
-    println!("");
+    println!();
     println!("Iterating from the given key...");
     // Reader::iter_from() iterates from the first key equal to or greater
     // than the given key.
-    let mut iter = reader.iter_from(store, "Japan").unwrap();
-    while let Some((country, city)) = iter.next() {
+    let mut iter = store.iter_from(&reader, "Japan").unwrap();
+    while let Some(Ok((country, city))) = iter.next() {
         println!("{}, {:?}", str::from_utf8(country).unwrap(), city);
     }
 
-    println!("");
+    println!();
     println!("Iterating from the given prefix...");
-    let mut iter = reader.iter_from(store, "Un").unwrap();
-    while let Some((country, city)) = iter.next() {
+    let mut iter = store.iter_from(&reader, "Un").unwrap();
+    while let Some(Ok((country, city))) = iter.next() {
         println!("{}, {:?}", str::from_utf8(country).unwrap(), city);
     }
 }
 
-fn populate_store(k: &Rkv, store: Store) -> Result<(), StoreError> {
+fn populate_store(k: &Rkv, store: SingleStore) -> Result<(), StoreError> {
     let mut writer = k.write()?;
     for (country, city) in vec![
         ("Canada", Value::Str("Ottawa")),
         ("United States of America", Value::Str("Washington")),
         ("Germany", Value::Str("Berlin")),
         ("France", Value::Str("Paris")),
         ("Italy", Value::Str("Rome")),
         ("United Kingdom", Value::Str("London")),
         ("Japan", Value::Str("Tokyo")),
     ] {
-        writer.put(store, country, &city)?;
+        store.put(&mut writer, country, &city)?;
     }
     writer.commit()
 }
--- a/third_party/rust/rkv/examples/simple-store.rs
+++ b/third_party/rust/rkv/examples/simple-store.rs
@@ -2,118 +2,168 @@
 // http://creativecommons.org/publicdomain/zero/1.0/
 
 //! A simple rkv demo that showcases the basic usage (put/get/delete) of rkv.
 //!
 //! You can test this out by running:
 //!
 //!     cargo run --example simple-store
 
-extern crate rkv;
-extern crate tempfile;
-
 use rkv::{
     Manager,
+    MultiStore,
     Rkv,
+    StoreOptions,
     Value,
+    Writer,
 };
 use tempfile::Builder;
 
 use std::fs;
 
+fn getput<'env, 's>(store: MultiStore, writer: &'env mut Writer, ids: &'s mut Vec<String>) {
+    let keys = vec!["str1", "str2", "str3"];
+    // we convert the writer into a cursor so that we can safely read
+    for k in keys.iter() {
+        // this is a multi-valued database, so get returns an iterator
+        let mut iter = store.get(writer, k).unwrap();
+        while let Some(Ok((_key, val))) = iter.next() {
+            if let Value::Str(s) = val.unwrap() {
+                ids.push(s.to_owned());
+            } else {
+                panic!("didn't get a string back!");
+            }
+        }
+    }
+    for id in ids {
+        store.put(writer, &id, &Value::Blob(b"weeeeeee")).unwrap();
+    }
+}
+
+fn delete(store: MultiStore, writer: &mut Writer) {
+    let keys = vec!["str1", "str2", "str3"];
+    let vals = vec!["string uno", "string quatro", "string siete"];
+    // we convert the writer into a cursor so that we can safely read
+    for i in 0..keys.len() {
+        store.delete(writer, &keys[i], &Value::Str(vals[i])).unwrap();
+    }
+}
+
 fn main() {
     let root = Builder::new().prefix("simple-db").tempdir().unwrap();
     fs::create_dir_all(root.path()).unwrap();
     let p = root.path();
 
     // The manager enforces that each process opens the same lmdb environment at most once
     let created_arc = Manager::singleton().write().unwrap().get_or_create(p, Rkv::new).unwrap();
     let k = created_arc.read().unwrap();
 
     // Creates a store called "store"
-    let store = k.open_or_create("store").unwrap();
+    let store = k.open_single("store", StoreOptions::create()).unwrap();
+
+    let multistore = k.open_multi("multistore", StoreOptions::create()).unwrap();
 
     println!("Inserting data...");
     {
         // Use a writer to mutate the store
         let mut writer = k.write().unwrap();
-        writer.put(store, "int", &Value::I64(1234)).unwrap();
-        writer.put(store, "uint", &Value::U64(1234_u64)).unwrap();
-        writer.put(store, "float", &Value::F64(1234.0.into())).unwrap();
-        writer.put(store, "instant", &Value::Instant(1528318073700)).unwrap();
-        writer.put(store, "boolean", &Value::Bool(true)).unwrap();
-        writer.put(store, "string", &Value::Str("héllo, yöu")).unwrap();
-        writer.put(store, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
-        writer.put(store, "blob", &Value::Blob(b"blob")).unwrap();
+        store.put(&mut writer, "int", &Value::I64(1234)).unwrap();
+        store.put(&mut writer, "uint", &Value::U64(1234_u64)).unwrap();
+        store.put(&mut writer, "float", &Value::F64(1234.0.into())).unwrap();
+        store.put(&mut writer, "instant", &Value::Instant(1_528_318_073_700)).unwrap();
+        store.put(&mut writer, "boolean", &Value::Bool(true)).unwrap();
+        store.put(&mut writer, "string", &Value::Str("héllo, yöu")).unwrap();
+        store.put(&mut writer, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
+        store.put(&mut writer, "blob", &Value::Blob(b"blob")).unwrap();
         writer.commit().unwrap();
     }
 
+    println!("Testing getput");
+    {
+        let mut ids = Vec::new();
+        let mut writer = k.write().unwrap();
+        multistore.put(&mut writer, "str1", &Value::Str("string uno")).unwrap();
+        multistore.put(&mut writer, "str1", &Value::Str("string dos")).unwrap();
+        multistore.put(&mut writer, "str1", &Value::Str("string tres")).unwrap();
+        multistore.put(&mut writer, "str2", &Value::Str("string quatro")).unwrap();
+        multistore.put(&mut writer, "str2", &Value::Str("string cinco")).unwrap();
+        multistore.put(&mut writer, "str2", &Value::Str("string seis")).unwrap();
+        multistore.put(&mut writer, "str3", &Value::Str("string siete")).unwrap();
+        multistore.put(&mut writer, "str3", &Value::Str("string ocho")).unwrap();
+        multistore.put(&mut writer, "str3", &Value::Str("string nueve")).unwrap();
+        getput(multistore, &mut writer, &mut ids);
+        writer.commit().unwrap();
+        let mut writer = k.write().unwrap();
+        delete(multistore, &mut writer);
+        writer.commit().unwrap();
+    }
     println!("Looking up keys...");
     {
         // Use a reader to query the store
         let reader = k.read().unwrap();
-        println!("Get int {:?}", reader.get(store, "int").unwrap());
-        println!("Get uint {:?}", reader.get(store, "uint").unwrap());
-        println!("Get float {:?}", reader.get(store, "float").unwrap());
-        println!("Get instant {:?}", reader.get(store, "instant").unwrap());
-        println!("Get boolean {:?}", reader.get(store, "boolean").unwrap());
-        println!("Get string {:?}", reader.get(store, "string").unwrap());
-        println!("Get json {:?}", reader.get(store, "json").unwrap());
-        println!("Get blob {:?}", reader.get(store, "blob").unwrap());
-        println!("Get non-existent {:?}", reader.get(store, "non-existent").unwrap());
+        println!("Get int {:?}", store.get(&reader, "int").unwrap());
+        println!("Get uint {:?}", store.get(&reader, "uint").unwrap());
+        println!("Get float {:?}", store.get(&reader, "float").unwrap());
+        println!("Get instant {:?}", store.get(&reader, "instant").unwrap());
+        println!("Get boolean {:?}", store.get(&reader, "boolean").unwrap());
+        println!("Get string {:?}", store.get(&reader, "string").unwrap());
+        println!("Get json {:?}", store.get(&reader, "json").unwrap());
+        println!("Get blob {:?}", store.get(&reader, "blob").unwrap());
+        println!("Get non-existent {:?}", store.get(&reader, "non-existent").unwrap());
     }
 
     println!("Looking up keys via Writer.get()...");
     {
         let mut writer = k.write().unwrap();
-        writer.put(store, "foo", &Value::Str("bar")).unwrap();
-        writer.put(store, "bar", &Value::Str("baz")).unwrap();
-        writer.delete(store, "foo").unwrap();
-        println!("It should be None! ({:?})", writer.get(store, "foo").unwrap());
-        println!("Get bar ({:?})", writer.get(store, "bar").unwrap());
+        store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
+        store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
+        store.delete(&mut writer, "foo").unwrap();
+        println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
+        println!("Get bar ({:?})", store.get(&writer, "bar").unwrap());
         writer.commit().unwrap();
         let reader = k.read().expect("reader");
-        println!("It should be None! ({:?})", reader.get(store, "foo").unwrap());
-        println!("Get bar {:?}", reader.get(store, "bar").unwrap());
+        println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
+        println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
     }
 
     println!("Aborting transaction...");
     {
         // Aborting a write transaction rollbacks the change(s)
         let mut writer = k.write().unwrap();
-        writer.put(store, "foo", &Value::Str("bar")).unwrap();
+        store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
         writer.abort();
 
         let reader = k.read().expect("reader");
-        println!("It should be None! ({:?})", reader.get(store, "foo").unwrap());
+        println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
         // Explicitly aborting a transaction is not required unless an early
         // abort is desired, since both read and write transactions will
         // implicitly be aborted once they go out of scope.
     }
 
     println!("Deleting keys...");
     {
         // Deleting a key/value also requires a write transaction
         let mut writer = k.write().unwrap();
-        writer.put(store, "foo", &Value::Str("bar")).unwrap();
-        writer.delete(store, "foo").unwrap();
-        println!("It should be None! ({:?})", writer.get(store, "foo").unwrap());
+        store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
+        store.delete(&mut writer, "foo").unwrap();
+        println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
         writer.commit().unwrap();
 
         // Committing a transaction consumes the writer, preventing you
         // from reusing it by failing and reporting a compile-time error.
         // This line would report error[E0382]: use of moved value: `writer`.
-        // writer.put(store, "baz", &Value::Str("buz")).unwrap();
+        // store.put(&mut writer, "baz", &Value::Str("buz")).unwrap();
     }
 
     println!("Write and read on multiple stores...");
     {
-        let another_store = k.open_or_create("another_store").unwrap();
+        let another_store = k.open_single("another_store", StoreOptions::create()).unwrap();
         let mut writer = k.write().unwrap();
-        writer.put(store, "foo", &Value::Str("bar")).unwrap();
-        writer.put(another_store, "foo", &Value::Str("baz")).unwrap();
+        store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
+        another_store.put(&mut writer, "foo", &Value::Str("baz")).unwrap();
         writer.commit().unwrap();
 
         let reader = k.read().unwrap();
-        println!("Get from store value: {:?}", reader.get(store, "foo").unwrap());
-        println!("Get from another store value: {:?}", reader.get(another_store, "foo").unwrap());
+        println!("Get from store value: {:?}", store.get(&reader, "foo").unwrap());
+        println!("Get from another store value: {:?}", another_store.get(&reader, "foo").unwrap());
     }
+    println!("Environment statistics: btree depth = {}", k.stat().unwrap().depth());
 }
--- a/third_party/rust/rkv/src/env.rs
+++ b/third_party/rust/rkv/src/env.rs
@@ -1,9 +1,9 @@
-// Copyright 2018 Mozilla
+// 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.
@@ -13,36 +13,37 @@ use std::os::raw::c_uint;
 use std::path::{
     Path,
     PathBuf,
 };
 
 use lmdb;
 
 use lmdb::{
+    Database,
     DatabaseFlags,
     Environment,
     EnvironmentBuilder,
+    Stat,
 };
 
-use error::StoreError;
-
-use integer::{
-    IntegerReader,
+use crate::error::StoreError;
+use crate::readwrite::{
+    Reader,
+    Writer,
+};
+use crate::store::integer::{
     IntegerStore,
-    IntegerWriter,
-    Key,
     PrimitiveInt,
 };
 
-use readwrite::{
-    Reader,
-    Store,
-    Writer,
-};
+use crate::store::integermulti::MultiIntegerStore;
+use crate::store::multi::MultiStore;
+use crate::store::single::SingleStore;
+use crate::store::Options as StoreOptions;
 
 pub static DEFAULT_MAX_DBS: c_uint = 5;
 
 /// Wrapper around an `lmdb::Environment`.
 #[derive(Debug)]
 pub struct Rkv {
     path: PathBuf,
     env: Environment,
@@ -50,16 +51,17 @@ pub struct Rkv {
 
 /// Static methods.
 impl Rkv {
     pub fn environment_builder() -> EnvironmentBuilder {
         Environment::new()
     }
 
     /// Return a new Rkv environment that supports up to `DEFAULT_MAX_DBS` open databases.
+    #[allow(clippy::new_ret_no_self)]
     pub fn new(path: &Path) -> Result<Rkv, StoreError> {
         Rkv::with_capacity(path, DEFAULT_MAX_DBS)
     }
 
     /// Return a new Rkv environment from the provided builder.
     pub fn from_env(path: &Path, env: EnvironmentBuilder) -> Result<Rkv, StoreError> {
         if !path.is_dir() {
             return Err(StoreError::DirectoryDoesNotExistError(path.into()));
@@ -85,128 +87,147 @@ impl Rkv {
 
         // Future: set flags, maximum size, etc. here if necessary.
         Rkv::from_env(path, builder)
     }
 }
 
 /// Store creation methods.
 impl Rkv {
-    pub fn open_or_create_default(&self) -> Result<Store, StoreError> {
-        self.open_or_create(None)
-    }
-
-    pub fn open_or_create<'s, T>(&self, name: T) -> Result<Store, StoreError>
+    /// Create or Open an existing database in (&[u8] -> Single Value) mode.
+    /// Note: that create=true cannot be called concurrently with other operations
+    /// so if you are sure that the database exists, call this with create=false.
+    pub fn open_single<'s, T>(&self, name: T, opts: StoreOptions) -> Result<SingleStore, StoreError>
     where
         T: Into<Option<&'s str>>,
     {
-        let flags = DatabaseFlags::empty();
-        self.open_or_create_with_flags(name, flags)
+        self.open(name, opts).map(SingleStore::new)
     }
 
-    pub fn open_or_create_integer<'s, T>(&self, name: T) -> Result<IntegerStore, StoreError>
+    /// Create or Open an existing database in (Integer -> Single Value) mode.
+    /// Note: that create=true cannot be called concurrently with other operations
+    /// so if you are sure that the database exists, call this with create=false.
+    pub fn open_integer<'s, T, K: PrimitiveInt>(
+        &self,
+        name: T,
+        mut opts: StoreOptions,
+    ) -> Result<IntegerStore<K>, StoreError>
+    where
+        T: Into<Option<&'s str>>,
+    {
+        opts.flags.set(DatabaseFlags::INTEGER_KEY, true);
+        self.open(name, opts).map(IntegerStore::new)
+    }
+
+    /// Create or Open an existing database in (&[u8] -> Multiple Values) mode.
+    /// Note: that create=true cannot be called concurrently with other operations
+    /// so if you are sure that the database exists, call this with create=false.
+    pub fn open_multi<'s, T>(&self, name: T, mut opts: StoreOptions) -> Result<MultiStore, StoreError>
     where
         T: Into<Option<&'s str>>,
     {
-        let mut flags = DatabaseFlags::empty();
-        flags.toggle(DatabaseFlags::INTEGER_KEY);
-        let db = self.env.create_db(name.into(), flags).map_err(|e| match e {
-            lmdb::Error::BadRslot => StoreError::open_during_transaction(),
-            _ => e.into(),
-        })?;
-        Ok(IntegerStore::new(db))
+        opts.flags.set(DatabaseFlags::DUP_SORT, true);
+        self.open(name, opts).map(MultiStore::new)
     }
 
-    pub fn open_or_create_with_flags<'s, T>(&self, name: T, flags: DatabaseFlags) -> Result<Store, StoreError>
+    /// Create or Open an existing database in (Integer -> Multiple Values) mode.
+    /// Note: that create=true cannot be called concurrently with other operations
+    /// so if you are sure that the database exists, call this with create=false.
+    pub fn open_multi_integer<'s, T, K: PrimitiveInt>(
+        &self,
+        name: T,
+        mut opts: StoreOptions,
+    ) -> Result<MultiIntegerStore<K>, StoreError>
     where
         T: Into<Option<&'s str>>,
     {
-        let db = self.env.create_db(name.into(), flags).map_err(|e| match e {
-            lmdb::Error::BadRslot => StoreError::open_during_transaction(),
-            _ => e.into(),
-        })?;
-        Ok(Store::new(db))
+        opts.flags.set(DatabaseFlags::INTEGER_KEY, true);
+        opts.flags.set(DatabaseFlags::DUP_SORT, true);
+        self.open(name, opts).map(MultiIntegerStore::new)
     }
 
-    /// Open an existing database, unlike other `open_or_create_*` functions, it
-    /// opens the given database by using a read transaction, which means other
-    /// in-flight write transaction will not block this call. This is preferred
-    /// to be used in the read_only scenarios.
-    pub fn open<'s, T>(&self, name: T) -> Result<Store, StoreError>
+    fn open<'s, T>(&self, name: T, opts: StoreOptions) -> Result<Database, StoreError>
     where
         T: Into<Option<&'s str>>,
     {
-        let db = self.env.open_db(name.into()).map_err(|e| match e {
-            lmdb::Error::BadRslot => StoreError::open_during_transaction(),
-            _ => e.into(),
-        })?;
-        Ok(Store::new(db))
+        if opts.create {
+            self.env.create_db(name.into(), opts.flags).map_err(|e| match e {
+                lmdb::Error::BadRslot => StoreError::open_during_transaction(),
+                _ => e.into(),
+            })
+        } else {
+            self.env.open_db(name.into()).map_err(|e| match e {
+                lmdb::Error::BadRslot => StoreError::open_during_transaction(),
+                _ => e.into(),
+            })
+        }
     }
 }
 
 /// Read and write accessors.
 impl Rkv {
-    pub fn read<K>(&self) -> Result<Reader<K>, StoreError>
-    where
-        K: AsRef<[u8]>,
-    {
-        let txn = self.env.begin_ro_txn()?;
-        Ok(Reader::new(txn))
+    /// Create a read transaction.  There can be multiple concurrent readers
+    /// for an environment, up to the maximum specified by LMDB (default 126),
+    /// and you can open readers while a write transaction is active.
+    pub fn read(&self) -> Result<Reader, StoreError> {
+        Ok(Reader::new(self.env.begin_ro_txn().map_err(StoreError::from)?))
     }
 
-    pub fn write<K>(&self) -> Result<Writer<K>, StoreError>
-    where
-        K: AsRef<[u8]>,
-    {
-        let txn = self.env.begin_rw_txn()?;
-        Ok(Writer::new(txn))
-    }
-
-    pub fn read_int<K>(&self) -> Result<IntegerReader<K>, StoreError>
-    where
-        K: PrimitiveInt,
-    {
-        let reader = self.read::<Key<K>>()?;
-        Ok(IntegerReader::new(reader))
-    }
-
-    pub fn write_int<K>(&self) -> Result<IntegerWriter<K>, StoreError>
-    where
-        K: PrimitiveInt,
-    {
-        let write = self.write::<Key<K>>()?;
-        Ok(IntegerWriter::new(write))
+    /// Create a write transaction.  There can be only one write transaction
+    /// active at any given time, so trying to create a second one will block
+    /// until the first is committed or aborted.
+    pub fn write(&self) -> Result<Writer, StoreError> {
+        Ok(Writer::new(self.env.begin_rw_txn().map_err(StoreError::from)?))
     }
 }
 
+/// Other environment methods.
+impl Rkv {
+    /// Flush the data buffers to disk. This call is only useful, when the environment
+    /// was open with either `NO_SYNC`, `NO_META_SYNC` or `MAP_ASYNC` (see below).
+    /// The call is not valid if the environment was opened with `READ_ONLY`.
+    ///
+    /// Data is always written to disk when `transaction.commit()` is called,
+    /// but the operating system may keep it buffered.
+    /// LMDB always flushes the OS buffers upon commit as well,
+    /// unless the environment was opened with `NO_SYNC` or in part `NO_META_SYNC`.
+    ///
+    /// `force`: if true, force a synchronous flush.
+    /// Otherwise if the environment has the `NO_SYNC` flag set the flushes will be omitted,
+    /// and with `MAP_ASYNC` they will be asynchronous.
+    pub fn sync(&self, force: bool) -> Result<(), StoreError> {
+        self.env.sync(force).map_err(|e| e.into())
+    }
+
+    /// Retrieves statistics about this environment.
+    pub fn stat(&self) -> Result<Stat, StoreError> {
+        self.env.stat().map_err(|e| e.into())
+    }
+}
+
+#[allow(clippy::cyclomatic_complexity)]
 #[cfg(test)]
 mod tests {
-    extern crate byteorder;
-    extern crate tempfile;
-
-    use self::byteorder::{
+    use byteorder::{
         ByteOrder,
         LittleEndian,
     };
-
-    use self::tempfile::Builder;
-
     use std::{
         fs,
         str,
+        sync::{
+            Arc,
+            RwLock,
+        },
         thread,
     };
-
-    use std::sync::{
-        Arc,
-        RwLock,
-    };
+    use tempfile::Builder;
 
     use super::*;
-    use *;
+    use crate::*;
 
     /// We can't open a directory that doesn't exist.
     #[test]
     fn test_open_fails() {
         let root = Builder::new().prefix("test_open_fails").tempdir().expect("tempdir");
         assert!(root.path().exists());
 
         let nope = root.path().join("nope/");
@@ -217,22 +238,22 @@ mod tests {
             Some(StoreError::DirectoryDoesNotExistError(p)) => {
                 assert_eq!(pb, p);
             },
             _ => panic!("expected error"),
         };
     }
 
     fn check_rkv(k: &Rkv) {
-        let _ = k.open_or_create_default().expect("created default");
+        let _ = k.open_single("default", StoreOptions::create()).expect("created default");
 
-        let yyy = k.open_or_create("yyy").expect("opened");
+        let yyy = k.open_single("yyy", StoreOptions::create()).expect("opened");
         let reader = k.read().expect("reader");
 
-        let result = reader.get(yyy, "foo");
+        let result = yyy.get(&reader, "foo");
         assert_eq!(None, result.expect("success but no value"));
     }
 
     #[test]
     fn test_open() {
         let root = Builder::new().prefix("test_open").tempdir().expect("tempdir");
         println!("Root path: {:?}", root.path());
         fs::create_dir_all(root.path()).expect("dir created");
@@ -246,17 +267,17 @@ mod tests {
     #[test]
     fn test_open_from_env() {
         let root = Builder::new().prefix("test_open_from_env").tempdir().expect("tempdir");
         println!("Root path: {:?}", root.path());
         fs::create_dir_all(root.path()).expect("dir created");
         assert!(root.path().is_dir());
 
         let mut builder = Rkv::environment_builder();
-        builder.set_max_dbs(1);
+        builder.set_max_dbs(2);
         let k = Rkv::from_env(root.path(), builder).expect("rkv");
 
         check_rkv(&k);
     }
 
     #[test]
     #[should_panic(expected = "opened: LmdbError(DbsFull)")]
     fn test_open_with_capacity() {
@@ -269,17 +290,17 @@ mod tests {
 
         check_rkv(&k);
 
         // This panics with "opened: LmdbError(DbsFull)" because we specified
         // a capacity of one (database), and check_rkv already opened one
         // (plus the default database, which doesn't count against the limit).
         // This should really return an error rather than panicking, per
         // <https://github.com/mozilla/lmdb-rs/issues/6>.
-        let _zzz = k.open_or_create("zzz").expect("opened");
+        let _zzz = k.open_single("zzz", StoreOptions::create()).expect("opened");
     }
 
     fn get_larger_than_default_map_size_value() -> usize {
         // The LMDB C library and lmdb Rust crate docs for setting the map size
         // <http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5>
         // <https://docs.rs/lmdb/0.8.0/lmdb/struct.EnvironmentBuilder.html#method.set_map_size>
         // both say that the default map size is 10,485,760 bytes, i.e. 10MiB.
         //
@@ -294,317 +315,355 @@ mod tests {
     #[should_panic(expected = "wrote: LmdbError(MapFull)")]
     fn test_exceed_map_size() {
         let root = Builder::new().prefix("test_exceed_map_size").tempdir().expect("tempdir");
         println!("Root path: {:?}", root.path());
         fs::create_dir_all(root.path()).expect("dir created");
         assert!(root.path().is_dir());
 
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create_default().expect("opened");
+        let sk: SingleStore = k.open_single("test", StoreOptions::create()).expect("opened");
 
         // Writing a large enough value should cause LMDB to fail on MapFull.
         // We write a string that is larger than the default map size.
         let val = "x".repeat(get_larger_than_default_map_size_value());
         let mut writer = k.write().expect("writer");
-        writer.put(sk, "foo", &Value::Str(&val)).expect("wrote");
+        sk.put(&mut writer, "foo", &Value::Str(&val)).expect("wrote");
     }
 
     #[test]
     fn test_increase_map_size() {
         let root = Builder::new().prefix("test_open_with_map_size").tempdir().expect("tempdir");
         println!("Root path: {:?}", root.path());
         fs::create_dir_all(root.path()).expect("dir created");
         assert!(root.path().is_dir());
 
         let mut builder = Rkv::environment_builder();
         // Set the map size to the size of the value we'll store in it + 100KiB,
         // which ensures that there's enough space for the value and metadata.
         builder.set_map_size(get_larger_than_default_map_size_value() + 100 * 1024 /* 100KiB */);
+        builder.set_max_dbs(2);
         let k = Rkv::from_env(root.path(), builder).unwrap();
-        let sk: Store = k.open_or_create_default().expect("opened");
+        let sk: SingleStore = k.open_single("test", StoreOptions::create()).expect("opened");
         let val = "x".repeat(get_larger_than_default_map_size_value());
 
         let mut writer = k.write().expect("writer");
-        writer.put(sk, "foo", &Value::Str(&val)).expect("wrote");
+        sk.put(&mut writer, "foo", &Value::Str(&val)).expect("wrote");
         writer.commit().expect("committed");
 
         let reader = k.read().unwrap();
-        assert_eq!(reader.get(sk, "foo").expect("read"), Some(Value::Str(&val)));
+        assert_eq!(sk.get(&reader, "foo").expect("read"), Some(Value::Str(&val)));
     }
 
     #[test]
     fn test_round_trip_and_transactions() {
         let root = Builder::new().prefix("test_round_trip_and_transactions").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
 
-        let sk: Store = k.open_or_create("sk").expect("opened");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
 
         {
             let mut writer = k.write().expect("writer");
-            writer.put(sk, "foo", &Value::I64(1234)).expect("wrote");
-            writer.put(sk, "noo", &Value::F64(1234.0.into())).expect("wrote");
-            writer.put(sk, "bar", &Value::Bool(true)).expect("wrote");
-            writer.put(sk, "baz", &Value::Str("héllo, yöu")).expect("wrote");
-            assert_eq!(writer.get(sk, "foo").expect("read"), Some(Value::I64(1234)));
-            assert_eq!(writer.get(sk, "noo").expect("read"), Some(Value::F64(1234.0.into())));
-            assert_eq!(writer.get(sk, "bar").expect("read"), Some(Value::Bool(true)));
-            assert_eq!(writer.get(sk, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
+            sk.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+            sk.put(&mut writer, "noo", &Value::F64(1234.0.into())).expect("wrote");
+            sk.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
+            sk.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
+            assert_eq!(sk.get(&writer, "foo").expect("read"), Some(Value::I64(1234)));
+            assert_eq!(sk.get(&writer, "noo").expect("read"), Some(Value::F64(1234.0.into())));
+            assert_eq!(sk.get(&writer, "bar").expect("read"), Some(Value::Bool(true)));
+            assert_eq!(sk.get(&writer, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
 
             // Isolation. Reads won't return values.
             let r = &k.read().unwrap();
-            assert_eq!(r.get(sk, "foo").expect("read"), None);
-            assert_eq!(r.get(sk, "bar").expect("read"), None);
-            assert_eq!(r.get(sk, "baz").expect("read"), None);
+            assert_eq!(sk.get(r, "foo").expect("read"), None);
+            assert_eq!(sk.get(r, "bar").expect("read"), None);
+            assert_eq!(sk.get(r, "baz").expect("read"), None);
         }
 
         // Dropped: tx rollback. Reads will still return nothing.
 
         {
             let r = &k.read().unwrap();
-            assert_eq!(r.get(sk, "foo").expect("read"), None);
-            assert_eq!(r.get(sk, "bar").expect("read"), None);
-            assert_eq!(r.get(sk, "baz").expect("read"), None);
+            assert_eq!(sk.get(r, "foo").expect("read"), None);
+            assert_eq!(sk.get(r, "bar").expect("read"), None);
+            assert_eq!(sk.get(r, "baz").expect("read"), None);
         }
 
         {
             let mut writer = k.write().expect("writer");
-            writer.put(sk, "foo", &Value::I64(1234)).expect("wrote");
-            writer.put(sk, "bar", &Value::Bool(true)).expect("wrote");
-            writer.put(sk, "baz", &Value::Str("héllo, yöu")).expect("wrote");
-            assert_eq!(writer.get(sk, "foo").expect("read"), Some(Value::I64(1234)));
-            assert_eq!(writer.get(sk, "bar").expect("read"), Some(Value::Bool(true)));
-            assert_eq!(writer.get(sk, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
+            sk.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+            sk.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
+            sk.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
+            assert_eq!(sk.get(&writer, "foo").expect("read"), Some(Value::I64(1234)));
+            assert_eq!(sk.get(&writer, "bar").expect("read"), Some(Value::Bool(true)));
+            assert_eq!(sk.get(&writer, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
 
             writer.commit().expect("committed");
         }
 
         // Committed. Reads will succeed.
         {
             let r = k.read().unwrap();
-            assert_eq!(r.get(sk, "foo").expect("read"), Some(Value::I64(1234)));
-            assert_eq!(r.get(sk, "bar").expect("read"), Some(Value::Bool(true)));
-            assert_eq!(r.get(sk, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
+            assert_eq!(sk.get(&r, "foo").expect("read"), Some(Value::I64(1234)));
+            assert_eq!(sk.get(&r, "bar").expect("read"), Some(Value::Bool(true)));
+            assert_eq!(sk.get(&r, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
         }
 
         {
             let mut writer = k.write().expect("writer");
-            writer.delete(sk, "foo").expect("deleted");
-            writer.delete(sk, "bar").expect("deleted");
-            writer.delete(sk, "baz").expect("deleted");
-            assert_eq!(writer.get(sk, "foo").expect("read"), None);
-            assert_eq!(writer.get(sk, "bar").expect("read"), None);
-            assert_eq!(writer.get(sk, "baz").expect("read"), None);
+            sk.delete(&mut writer, "foo").expect("deleted");
+            sk.delete(&mut writer, "bar").expect("deleted");
+            sk.delete(&mut writer, "baz").expect("deleted");
+            assert_eq!(sk.get(&writer, "foo").expect("read"), None);
+            assert_eq!(sk.get(&writer, "bar").expect("read"), None);
+            assert_eq!(sk.get(&writer, "baz").expect("read"), None);
 
             // Isolation. Reads still return values.
             let r = k.read().unwrap();
-            assert_eq!(r.get(sk, "foo").expect("read"), Some(Value::I64(1234)));
-            assert_eq!(r.get(sk, "bar").expect("read"), Some(Value::Bool(true)));
-            assert_eq!(r.get(sk, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
+            assert_eq!(sk.get(&r, "foo").expect("read"), Some(Value::I64(1234)));
+            assert_eq!(sk.get(&r, "bar").expect("read"), Some(Value::Bool(true)));
+            assert_eq!(sk.get(&r, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
         }
 
         // Dropped: tx rollback. Reads will still return values.
 
         {
             let r = k.read().unwrap();
-            assert_eq!(r.get(sk, "foo").expect("read"), Some(Value::I64(1234)));
-            assert_eq!(r.get(sk, "bar").expect("read"), Some(Value::Bool(true)));
-            assert_eq!(r.get(sk, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
+            assert_eq!(sk.get(&r, "foo").expect("read"), Some(Value::I64(1234)));
+            assert_eq!(sk.get(&r, "bar").expect("read"), Some(Value::Bool(true)));
+            assert_eq!(sk.get(&r, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
         }
 
         {
             let mut writer = k.write().expect("writer");
-            writer.delete(sk, "foo").expect("deleted");
-            writer.delete(sk, "bar").expect("deleted");
-            writer.delete(sk, "baz").expect("deleted");
-            assert_eq!(writer.get(sk, "foo").expect("read"), None);
-            assert_eq!(writer.get(sk, "bar").expect("read"), None);
-            assert_eq!(writer.get(sk, "baz").expect("read"), None);
+            sk.delete(&mut writer, "foo").expect("deleted");
+            sk.delete(&mut writer, "bar").expect("deleted");
+            sk.delete(&mut writer, "baz").expect("deleted");
+            assert_eq!(sk.get(&writer, "foo").expect("read"), None);
+            assert_eq!(sk.get(&writer, "bar").expect("read"), None);
+            assert_eq!(sk.get(&writer, "baz").expect("read"), None);
 
             writer.commit().expect("committed");
         }
 
         // Committed. Reads will succeed but return None to indicate a missing value.
         {
             let r = k.read().unwrap();
-            assert_eq!(r.get(sk, "foo").expect("read"), None);
-            assert_eq!(r.get(sk, "bar").expect("read"), None);
-            assert_eq!(r.get(sk, "baz").expect("read"), None);
+            assert_eq!(sk.get(&r, "foo").expect("read"), None);
+            assert_eq!(sk.get(&r, "bar").expect("read"), None);
+            assert_eq!(sk.get(&r, "baz").expect("read"), None);
         }
     }
 
     #[test]
+    fn test_multi_put_get_del() {
+        let root = Builder::new().prefix("test_multi_put_get_del").tempdir().expect("tempdir");
+        fs::create_dir_all(root.path()).expect("dir created");
+        let k = Rkv::new(root.path()).expect("new succeeded");
+        let multistore = k.open_multi("multistore", StoreOptions::create()).unwrap();
+        let mut writer = k.write().unwrap();
+        multistore.put(&mut writer, "str1", &Value::Str("str1 foo")).unwrap();
+        multistore.put(&mut writer, "str1", &Value::Str("str1 bar")).unwrap();
+        multistore.put(&mut writer, "str2", &Value::Str("str2 foo")).unwrap();
+        multistore.put(&mut writer, "str2", &Value::Str("str2 bar")).unwrap();
+        multistore.put(&mut writer, "str3", &Value::Str("str3 foo")).unwrap();
+        multistore.put(&mut writer, "str3", &Value::Str("str3 bar")).unwrap();
+        writer.commit().unwrap();
+        let writer = k.write().unwrap();
+        {
+            let mut iter = multistore.get(&writer, "str1").unwrap();
+            let (id, val) = iter.next().unwrap().unwrap();
+            assert_eq!((id, val), (&b"str1"[..], Some(Value::Str("str1 bar"))));
+            let (id, val) = iter.next().unwrap().unwrap();
+            assert_eq!((id, val), (&b"str1"[..], Some(Value::Str("str1 foo"))));
+        }
+        writer.commit().unwrap();
+        let mut writer = k.write().unwrap();
+
+        multistore.delete(&mut writer, "str1", &Value::Str("str1 foo")).unwrap();
+        assert_eq!(multistore.get_first(&writer, "str1").unwrap(), Some(Value::Str("str1 bar")));
+
+        multistore.delete(&mut writer, "str2", &Value::Str("str2 bar")).unwrap();
+        assert_eq!(multistore.get_first(&writer, "str2").unwrap(), Some(Value::Str("str2 foo")));
+
+        multistore.delete_all(&mut writer, "str3").unwrap();
+        assert_eq!(multistore.get_first(&writer, "str3").unwrap(), None);
+        writer.commit().unwrap();
+    }
+
+    #[test]
     fn test_open_store_for_read() {
         let root = Builder::new().prefix("test_open_store_for_read").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
         // First create the store, and start a write transaction on it.
-        let sk = k.open_or_create("sk").expect("opened");
+        let sk = k.open_single("sk", StoreOptions::create()).expect("opened");
         let mut writer = k.write().expect("writer");
-        writer.put(sk, "foo", &Value::Str("bar")).expect("write");
+        sk.put(&mut writer, "foo", &Value::Str("bar")).expect("write");
 
         // Open the same store for read, note that the write transaction is still in progress,
         // it should not block the reader though.
-        let sk_readonly = k.open("sk").expect("opened");
+        let sk_readonly = k.open_single("sk", StoreOptions::default()).expect("opened");
         writer.commit().expect("commit");
         // Now the write transaction is committed, any followed reads should see its change.
         let reader = k.read().expect("reader");
-        assert_eq!(reader.get(sk_readonly, "foo").expect("read"), Some(Value::Str("bar")));
+        assert_eq!(sk_readonly.get(&reader, "foo").expect("read"), Some(Value::Str("bar")));
     }
 
     #[test]
     #[should_panic(expected = "open a missing store")]
     fn test_open_a_missing_store() {
         let root = Builder::new().prefix("test_open_a_missing_store").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let _sk = k.open("sk").expect("open a missing store");
+        let _sk = k.open("sk", StoreOptions::default()).expect("open a missing store");
     }
 
     #[test]
     fn test_open_fail_with_badrslot() {
         let root = Builder::new().prefix("test_open_fail_with_badrslot").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
         // First create the store
-        let _sk = k.open_or_create("sk").expect("opened");
+        let _sk = k.open_single("sk", StoreOptions::create()).expect("opened");
         // Open a reader on this store
-        let _reader = k.read::<&str>().expect("reader");
+        let _reader = k.read().expect("reader");
         // Open the same store for read while the reader is in progress will panic
-        let store: Result<Store, StoreError> = k.open("sk");
+        let store: Result<SingleStore, StoreError> = k.open_single("sk", StoreOptions::default());
         match store {
             Err(StoreError::OpenAttemptedDuringTransaction(_thread_id)) => assert!(true),
             _ => panic!("should panic"),
         }
     }
 
     #[test]
     fn test_read_before_write_num() {
         let root = Builder::new().prefix("test_read_before_write_num").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create("sk").expect("opened");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
 
         // Test reading a number, modifying it, and then writing it back.
         // We have to be done with the Value::I64 before calling Writer::put,
         // as the Value::I64 borrows an immutable reference to the Writer.
         // So we extract and copy its primitive value.
 
-        fn get_existing_foo(writer: &Writer<&str>, store: Store) -> Option<i64> {
-            match writer.get(store, "foo").expect("read") {
+        fn get_existing_foo(writer: &Writer, store: SingleStore) -> Option<i64> {
+            match store.get(writer, "foo").expect("read") {
                 Some(Value::I64(val)) => Some(val),
                 _ => None,
             }
         }
 
         let mut writer = k.write().expect("writer");
         let mut existing = get_existing_foo(&writer, sk).unwrap_or(99);
         existing += 1;
-        writer.put(sk, "foo", &Value::I64(existing)).expect("success");
+        sk.put(&mut writer, "foo", &Value::I64(existing)).expect("success");
 
         let updated = get_existing_foo(&writer, sk).unwrap_or(99);
         assert_eq!(updated, 100);
         writer.commit().expect("commit");
     }
 
     #[test]
     fn test_read_before_write_str() {
         let root = Builder::new().prefix("test_read_before_write_str").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create("sk").expect("opened");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
 
         // Test reading a string, modifying it, and then writing it back.
         // We have to be done with the Value::Str before calling Writer::put,
         // as the Value::Str (and its underlying &str) borrows an immutable
         // reference to the Writer.  So we copy it to a String.
 
         let mut writer = k.write().expect("writer");
-        let mut existing = match writer.get(sk, "foo").expect("read") {
+        let mut existing = match sk.get(&writer, "foo").expect("read") {
             Some(Value::Str(val)) => val,
             _ => "",
-        }.to_string();
+        }
+        .to_string();
         existing.push('…');
-        writer.put(sk, "foo", &Value::Str(&existing)).expect("write");
+        sk.put(&mut writer, "foo", &Value::Str(&existing)).expect("write");
         writer.commit().expect("commit");
     }
 
     #[test]
     fn test_concurrent_read_transactions_prohibited() {
         let root = Builder::new().prefix("test_concurrent_reads_prohibited").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
 
-        let _first = k.read::<&str>().expect("reader");
-        let second = k.read::<&str>();
+        let _first = k.read().expect("reader");
+        let second = k.read();
 
         match second {
             Err(StoreError::ReadTransactionAlreadyExists(t)) => {
                 println!("Thread was {:?}", t);
             },
             _ => {
                 panic!("Expected error.");
             },
         }
     }
 
     #[test]
     fn test_isolation() {
         let root = Builder::new().prefix("test_isolation").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let s: Store = k.open_or_create("s").expect("opened");
+        let s: SingleStore = k.open_single("s", StoreOptions::create()).expect("opened");
 
         // Add one field.
         {
             let mut writer = k.write().expect("writer");
-            writer.put(s, "foo", &Value::I64(1234)).expect("wrote");
+            s.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
             writer.commit().expect("committed");
         }
 
         {
             let reader = k.read().unwrap();
-            assert_eq!(reader.get(s, "foo").expect("read"), Some(Value::I64(1234)));
+            assert_eq!(s.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
         }
 
         // Establish a long-lived reader that outlasts a writer.
         let reader = k.read().expect("reader");
-        assert_eq!(reader.get(s, "foo").expect("read"), Some(Value::I64(1234)));
+        assert_eq!(s.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
 
         // Start a write transaction.
         let mut writer = k.write().expect("writer");
-        writer.put(s, "foo", &Value::I64(999)).expect("wrote");
+        s.put(&mut writer, "foo", &Value::I64(999)).expect("wrote");
 
         // The reader and writer are isolated.
-        assert_eq!(reader.get(s, "foo").expect("read"), Some(Value::I64(1234)));
-        assert_eq!(writer.get(s, "foo").expect("read"), Some(Value::I64(999)));
+        assert_eq!(s.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
+        assert_eq!(s.get(&writer, "foo").expect("read"), Some(Value::I64(999)));
 
         // If we commit the writer, we still have isolation.
         writer.commit().expect("committed");
-        assert_eq!(reader.get(s, "foo").expect("read"), Some(Value::I64(1234)));
+        assert_eq!(s.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
 
         // A new reader sees the committed value. Note that LMDB doesn't allow two
         // read transactions to exist in the same thread, so we abort the previous one.
         reader.abort();
         let reader = k.read().expect("reader");
-        assert_eq!(reader.get(s, "foo").expect("read"), Some(Value::I64(999)));
+        assert_eq!(s.get(&reader, "foo").expect("read"), Some(Value::I64(999)));
     }
 
     #[test]
     fn test_blob() {
         let root = Builder::new().prefix("test_round_trip_blob").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create("sk").expect("opened");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
         let mut writer = k.write().expect("writer");
 
-        assert_eq!(writer.get(sk, "foo").expect("read"), None);
-        writer.put(sk, "foo", &Value::Blob(&[1, 2, 3, 4])).expect("wrote");
-        assert_eq!(writer.get(sk, "foo").expect("read"), Some(Value::Blob(&[1, 2, 3, 4])));
+        assert_eq!(sk.get(&writer, "foo").expect("read"), None);
+        sk.put(&mut writer, "foo", &Value::Blob(&[1, 2, 3, 4])).expect("wrote");
+        assert_eq!(sk.get(&writer, "foo").expect("read"), Some(Value::Blob(&[1, 2, 3, 4])));
 
         fn u16_to_u8(src: &[u16]) -> Vec<u8> {
             let mut dst = vec![0; 2 * src.len()];
             LittleEndian::write_u16_into(src, &mut dst);
             dst
         }
 
         fn u8_to_u16(src: &[u8]) -> Vec<u16> {
@@ -612,322 +671,352 @@ mod tests {
             LittleEndian::read_u16_into(src, &mut dst);
             dst
         }
 
         // When storing UTF-16 strings as blobs, we'll need to convert
         // their [u16] backing storage to [u8].  Test that converting, writing,
         // reading, and converting back works as expected.
         let u16_array = [1000, 10000, 54321, 65535];
-        assert_eq!(writer.get(sk, "bar").expect("read"), None);
-        writer.put(sk, "bar", &Value::Blob(&u16_to_u8(&u16_array))).expect("wrote");
-        let u8_array = match writer.get(sk, "bar").expect("read") {
+        assert_eq!(sk.get(&writer, "bar").expect("read"), None);
+        sk.put(&mut writer, "bar", &Value::Blob(&u16_to_u8(&u16_array))).expect("wrote");
+        let u8_array = match sk.get(&writer, "bar").expect("read") {
             Some(Value::Blob(val)) => val,
             _ => &[],
         };
         assert_eq!(u8_to_u16(u8_array), u16_array);
     }
 
     #[test]
-    #[should_panic(expected = "not yet implemented")]
-    fn test_delete_value() {
-        let root = Builder::new().prefix("test_delete_value").tempdir().expect("tempdir");
+    fn test_sync() {
+        let root = Builder::new().prefix("test_sync").tempdir().expect("tempdir");
+        fs::create_dir_all(root.path()).expect("dir created");
+        let mut builder = Rkv::environment_builder();
+        builder.set_max_dbs(1);
+        builder.set_flags(EnvironmentFlags::NO_SYNC);
+        {
+            let k = Rkv::from_env(root.path(), builder).expect("new succeeded");
+            let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
+
+            {
+                let mut writer = k.write().expect("writer");
+                sk.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+                writer.commit().expect("committed");
+                k.sync(true).expect("synced");
+            }
+        }
+        let k = Rkv::from_env(root.path(), builder).expect("new succeeded");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::default()).expect("opened");
+        let reader = k.read().expect("reader");
+        assert_eq!(sk.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
+    }
+
+    #[test]
+    fn test_stat() {
+        let root = Builder::new().prefix("test_sync").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create_with_flags("sk", DatabaseFlags::DUP_SORT).expect("opened");
-
-        let mut writer = k.write().expect("writer");
-        writer.put(sk, "foo", &Value::I64(1234)).expect("wrote");
-        writer.put(sk, "foo", &Value::I64(1235)).expect("wrote");
-        writer.delete_value(sk, "foo", &Value::I64(1234)).expect("deleted");
+        for i in 0..5 {
+            let sk: IntegerStore<u32> =
+                k.open_integer(&format!("sk{}", i)[..], StoreOptions::create()).expect("opened");
+            {
+                let mut writer = k.write().expect("writer");
+                sk.put(&mut writer, i, &Value::I64(i64::from(i))).expect("wrote");
+                writer.commit().expect("committed");
+            }
+        }
+        assert_eq!(k.stat().expect("stat").depth(), 1);
+        assert_eq!(k.stat().expect("stat").entries(), 5);
+        assert_eq!(k.stat().expect("stat").branch_pages(), 0);
+        assert_eq!(k.stat().expect("stat").leaf_pages(), 1);
     }
 
     #[test]
     fn test_iter() {
         let root = Builder::new().prefix("test_iter").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create("sk").expect("opened");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
 
         // An iterator over an empty store returns no values.
         {
-            let reader = k.read::<&str>().unwrap();
-            let mut iter = reader.iter_start(sk).unwrap();
+            let reader = k.read().unwrap();
+            let mut iter = sk.iter_start(&reader).unwrap();
             assert!(iter.next().is_none());
         }
 
         let mut writer = k.write().expect("writer");
-        writer.put(sk, "foo", &Value::I64(1234)).expect("wrote");
-        writer.put(sk, "noo", &Value::F64(1234.0.into())).expect("wrote");
-        writer.put(sk, "bar", &Value::Bool(true)).expect("wrote");
-        writer.put(sk, "baz", &Value::Str("héllo, yöu")).expect("wrote");
-        writer.put(sk, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote");
-        writer.put(sk, "你好,遊客", &Value::Str("米克規則")).expect("wrote");
+        sk.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+        sk.put(&mut writer, "noo", &Value::F64(1234.0.into())).expect("wrote");
+        sk.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
+        sk.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
+        sk.put(&mut writer, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote");
+        sk.put(&mut writer, "你好,遊客", &Value::Str("米克規則")).expect("wrote");
         writer.commit().expect("committed");
 
         let reader = k.read().unwrap();
 
         // Reader.iter() returns (key, value) tuples ordered by key.
-        let mut iter = reader.iter_start(sk).unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = sk.iter_start(&reader).unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "bar");
-        assert_eq!(val.expect("value"), Some(Value::Bool(true)));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Bool(true)));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "baz");
-        assert_eq!(val.expect("value"), Some(Value::Str("héllo, yöu")));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Str("héllo, yöu")));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "foo");
-        assert_eq!(val.expect("value"), Some(Value::I64(1234)));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::I64(1234)));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
-        assert_eq!(val.expect("value"), Some(Value::Str("Emil.RuleZ!")));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Iterators don't loop.  Once one returns None, additional calls
         // to its next() method will always return None.
         assert!(iter.next().is_none());
 
         // Reader.iter_from() begins iteration at the first key equal to
         // or greater than the given key.
-        let mut iter = reader.iter_from(sk, "moo").unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = sk.iter_from(&reader, "moo").unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Reader.iter_from() works as expected when the given key is a prefix
         // of a key in the store.
-        let mut iter = reader.iter_from(sk, "no").unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = sk.iter_from(&reader, "no").unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
     }
 
     #[test]
     fn test_iter_from_key_greater_than_existing() {
         let root = Builder::new().prefix("test_iter_from_key_greater_than_existing").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let sk: Store = k.open_or_create("sk").expect("opened");
+        let sk: SingleStore = k.open_single("sk", StoreOptions::create()).expect("opened");
 
         let mut writer = k.write().expect("writer");
-        writer.put(sk, "foo", &Value::I64(1234)).expect("wrote");
-        writer.put(sk, "noo", &Value::F64(1234.0.into())).expect("wrote");
-        writer.put(sk, "bar", &Value::Bool(true)).expect("wrote");
-        writer.put(sk, "baz", &Value::Str("héllo, yöu")).expect("wrote");
+        sk.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+        sk.put(&mut writer, "noo", &Value::F64(1234.0.into())).expect("wrote");
+        sk.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
+        sk.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
         writer.commit().expect("committed");
 
         let reader = k.read().unwrap();
-        let mut iter = reader.iter_from(sk, "nuu").unwrap();
+        let mut iter = sk.iter_from(&reader, "nuu").unwrap();
         assert!(iter.next().is_none());
     }
 
     #[test]
     fn test_multiple_store_read_write() {
         let root = Builder::new().prefix("test_multiple_store_read_write").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
 
-        let s1: Store = k.open_or_create("store_1").expect("opened");
-        let s2: Store = k.open_or_create("store_2").expect("opened");
-        let s3: Store = k.open_or_create("store_3").expect("opened");
+        let s1: SingleStore = k.open_single("store_1", StoreOptions::create()).expect("opened");
+        let s2: SingleStore = k.open_single("store_2", StoreOptions::create()).expect("opened");
+        let s3: SingleStore = k.open_single("store_3", StoreOptions::create()).expect("opened");
 
         let mut writer = k.write().expect("writer");
-        writer.put(s1, "foo", &Value::Str("bar")).expect("wrote");
-        writer.put(s2, "foo", &Value::I64(123)).expect("wrote");
-        writer.put(s3, "foo", &Value::Bool(true)).expect("wrote");
+        s1.put(&mut writer, "foo", &Value::Str("bar")).expect("wrote");
+        s2.put(&mut writer, "foo", &Value::I64(123)).expect("wrote");
+        s3.put(&mut writer, "foo", &Value::Bool(true)).expect("wrote");
 
-        assert_eq!(writer.get(s1, "foo").expect("read"), Some(Value::Str("bar")));
-        assert_eq!(writer.get(s2, "foo").expect("read"), Some(Value::I64(123)));
-        assert_eq!(writer.get(s3, "foo").expect("read"), Some(Value::Bool(true)));
+        assert_eq!(s1.get(&writer, "foo").expect("read"), Some(Value::Str("bar")));
+        assert_eq!(s2.get(&writer, "foo").expect("read"), Some(Value::I64(123)));
+        assert_eq!(s3.get(&writer, "foo").expect("read"), Some(Value::Bool(true)));
 
         writer.commit().expect("committed");
 
         let reader = k.read().expect("unbound_reader");
-        assert_eq!(reader.get(s1, "foo").expect("read"), Some(Value::Str("bar")));
-        assert_eq!(reader.get(s2, "foo").expect("read"), Some(Value::I64(123)));
-        assert_eq!(reader.get(s3, "foo").expect("read"), Some(Value::Bool(true)));
+        assert_eq!(s1.get(&reader, "foo").expect("read"), Some(Value::Str("bar")));
+        assert_eq!(s2.get(&reader, "foo").expect("read"), Some(Value::I64(123)));
+        assert_eq!(s3.get(&reader, "foo").expect("read"), Some(Value::Bool(true)));
         reader.abort();
 
         // test delete across multiple stores
         let mut writer = k.write().expect("writer");
-        writer.delete(s1, "foo").expect("deleted");
-        writer.delete(s2, "foo").expect("deleted");
-        writer.delete(s3, "foo").expect("deleted");
+        s1.delete(&mut writer, "foo").expect("deleted");
+        s2.delete(&mut writer, "foo").expect("deleted");
+        s3.delete(&mut writer, "foo").expect("deleted");
         writer.commit().expect("committed");
 
         let reader = k.read().expect("reader");
-        assert_eq!(reader.get(s1, "key").expect("value"), None);
-        assert_eq!(reader.get(s2, "key").expect("value"), None);
-        assert_eq!(reader.get(s3, "key").expect("value"), None);
+        assert_eq!(s1.get(&reader, "key").expect("value"), None);
+        assert_eq!(s2.get(&reader, "key").expect("value"), None);
+        assert_eq!(s3.get(&reader, "key").expect("value"), None);
     }
 
     #[test]
     fn test_multiple_store_iter() {
         let root = Builder::new().prefix("test_multiple_store_iter").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let k = Rkv::new(root.path()).expect("new succeeded");
-        let s1: Store = k.open_or_create("store_1").expect("opened");
-        let s2: Store = k.open_or_create("store_2").expect("opened");
+        let s1: SingleStore = k.open_single("store_1", StoreOptions::create()).expect("opened");
+        let s2: SingleStore = k.open_single("store_2", StoreOptions::create()).expect("opened");
 
         let mut writer = k.write().expect("writer");
         // Write to "s1"
-        writer.put(s1, "foo", &Value::I64(1234)).expect("wrote");
-        writer.put(s1, "noo", &Value::F64(1234.0.into())).expect("wrote");
-        writer.put(s1, "bar", &Value::Bool(true)).expect("wrote");
-        writer.put(s1, "baz", &Value::Str("héllo, yöu")).expect("wrote");
-        writer.put(s1, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote");
-        writer.put(s1, "你好,遊客", &Value::Str("米克規則")).expect("wrote");
-        // Writer to "s2"
-        writer.put(s2, "foo", &Value::I64(1234)).expect("wrote");
-        writer.put(s2, "noo", &Value::F64(1234.0.into())).expect("wrote");
-        writer.put(s2, "bar", &Value::Bool(true)).expect("wrote");
-        writer.put(s2, "baz", &Value::Str("héllo, yöu")).expect("wrote");
-        writer.put(s2, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote");
-        writer.put(s2, "你好,遊客", &Value::Str("米克規則")).expect("wrote");
+        s1.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+        s1.put(&mut writer, "noo", &Value::F64(1234.0.into())).expect("wrote");
+        s1.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
+        s1.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
+        s1.put(&mut writer, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote");
+        s1.put(&mut writer, "你好,遊客", &Value::Str("米克規則")).expect("wrote");
+        // &mut writer to "s2"
+        s2.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
+        s2.put(&mut writer, "noo", &Value::F64(1234.0.into())).expect("wrote");
+        s2.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
+        s2.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
+        s2.put(&mut writer, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote");
+        s2.put(&mut writer, "你好,遊客", &Value::Str("米克規則")).expect("wrote");
         writer.commit().expect("committed");
 
         let reader = k.read().unwrap();
 
         // Iterate through the whole store in "s1"
-        let mut iter = reader.iter_start(s1).unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = s1.iter_start(&reader).unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "bar");
-        assert_eq!(val.expect("value"), Some(Value::Bool(true)));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Bool(true)));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "baz");
-        assert_eq!(val.expect("value"), Some(Value::Str("héllo, yöu")));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Str("héllo, yöu")));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "foo");
-        assert_eq!(val.expect("value"), Some(Value::I64(1234)));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::I64(1234)));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
-        assert_eq!(val.expect("value"), Some(Value::Str("Emil.RuleZ!")));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Iterate through the whole store in "s2"
-        let mut iter = reader.iter_start(s2).unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = s2.iter_start(&reader).unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "bar");
-        assert_eq!(val.expect("value"), Some(Value::Bool(true)));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Bool(true)));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "baz");
-        assert_eq!(val.expect("value"), Some(Value::Str("héllo, yöu")));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Str("héllo, yöu")));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "foo");
-        assert_eq!(val.expect("value"), Some(Value::I64(1234)));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::I64(1234)));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
-        assert_eq!(val.expect("value"), Some(Value::Str("Emil.RuleZ!")));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Iterate from a given key in "s1"
-        let mut iter = reader.iter_from(s1, "moo").unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = s1.iter_from(&reader, "moo").unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Iterate from a given key in "s2"
-        let mut iter = reader.iter_from(s2, "moo").unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = s2.iter_from(&reader, "moo").unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Iterate from a given prefix in "s1"
-        let mut iter = reader.iter_from(s1, "no").unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = s1.iter_from(&reader, "no").unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
 
         // Iterate from a given prefix in "s2"
-        let mut iter = reader.iter_from(s2, "no").unwrap();
-        let (key, val) = iter.next().unwrap();
+        let mut iter = s2.iter_from(&reader, "no").unwrap();
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "noo");
-        assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into())));
-        let (key, val) = iter.next().unwrap();
+        assert_eq!(val, Some(Value::F64(1234.0.into())));
+        let (key, val) = iter.next().unwrap().unwrap();
         assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
-        assert_eq!(val.expect("value"), Some(Value::Str("米克規則")));
+        assert_eq!(val, Some(Value::Str("米克規則")));
         assert!(iter.next().is_none());
     }
 
     #[test]
     fn test_store_multiple_thread() {
         let root = Builder::new().prefix("test_multiple_thread").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
         let rkv_arc = Arc::new(RwLock::new(Rkv::new(root.path()).expect("new succeeded")));
-        let store = rkv_arc.read().unwrap().open_or_create_default().expect("opened");
+        let store = rkv_arc.read().unwrap().open_single("test", StoreOptions::create()).expect("opened");
 
         let num_threads = 10;
         let mut write_handles = Vec::with_capacity(num_threads as usize);
         let mut read_handles = Vec::with_capacity(num_threads as usize);
 
         // Note that this isn't intended to demonstrate a good use of threads.
         // For this shape of data, it would be more performant to write/read
         // all values using one transaction in a single thread. The point here
         // is just to confirm that a store can be shared by multiple threads.
 
         // For each KV pair, spawn a thread that writes it to the store.
         for i in 0..num_threads {
             let rkv_arc = rkv_arc.clone();
             write_handles.push(thread::spawn(move || {
                 let rkv = rkv_arc.write().expect("rkv");
                 let mut writer = rkv.write().expect("writer");
-                writer.put(store, i.to_string(), &Value::U64(i)).expect("written");
+                store.put(&mut writer, i.to_string(), &Value::U64(i)).expect("written");
                 writer.commit().unwrap();
             }));
         }
         for handle in write_handles {
             handle.join().expect("joined");
         }
 
         // For each KV pair, spawn a thread that reads it from the store
         // and returns its value.
         for i in 0..num_threads {
             let rkv_arc = rkv_arc.clone();
             read_handles.push(thread::spawn(move || {
                 let rkv = rkv_arc.read().expect("rkv");
                 let reader = rkv.read().expect("reader");
-                let value = match reader.get(store, i.to_string()) {
+                let value = match store.get(&reader, i.to_string()) {
                     Ok(Some(Value::U64(value))) => value,
                     Ok(Some(_)) => panic!("value type unexpected"),
                     Ok(None) => panic!("value not found"),
                     Err(err) => panic!(err),
                 };
                 assert_eq!(value, i);
                 value
             }));
--- a/third_party/rust/rkv/src/error.rs
+++ b/third_party/rust/rkv/src/error.rs
@@ -1,24 +1,25 @@
-// Copyright 2018 Mozilla
+// 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.
 
 use std::path::PathBuf;
 
 use bincode;
+use failure::Fail;
 use lmdb;
 
-use value::Type;
+use crate::value::Type;
 
 #[derive(Debug, Fail)]
 pub enum DataError {
     #[fail(display = "unknown type tag: {}", _0)]
     UnknownType(u8),
 
     #[fail(display = "unexpected type tag: expected {}, got {}", expected, actual)]
     UnexpectedType {
deleted file mode 100644
--- a/third_party/rust/rkv/src/integer.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2018 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.
-
-use std::marker::PhantomData;
-
-use bincode::serialize;
-
-use serde::Serialize;
-
-use lmdb::Database;
-
-use error::{
-    DataError,
-    StoreError,
-};
-
-use value::Value;
-
-use readwrite::{
-    Reader,
-    Store,
-    Writer,
-};
-
-pub trait EncodableKey {
-    fn to_bytes(&self) -> Result<Vec<u8>, DataError>;
-}
-
-pub trait PrimitiveInt: EncodableKey {}
-
-impl PrimitiveInt for u32 {}
-
-impl<T> EncodableKey for T
-where
-    T: Serialize,
-{
-    fn to_bytes(&self) -> Result<Vec<u8>, DataError> {
-        serialize(self)         // TODO: limited key length.
-            .map_err(|e| e.into())
-    }
-}
-
-pub(crate) struct Key<K> {
-    bytes: Vec<u8>,
-    phantom: PhantomData<K>,
-}
-
-impl<K> AsRef<[u8]> for Key<K>
-where
-    K: EncodableKey,
-{
-    fn as_ref(&self) -> &[u8] {
-        self.bytes.as_ref()
-    }
-}
-
-impl<K> Key<K>
-where
-    K: EncodableKey,
-{
-    fn new(k: K) -> Result<Key<K>, DataError> {
-        Ok(Key {
-            bytes: k.to_bytes()?,
-            phantom: PhantomData,
-        })
-    }
-}
-
-pub struct IntegerReader<'env, K>
-where
-    K: PrimitiveInt,
-{
-    inner: Reader<'env, Key<K>>,
-}
-
-impl<'env, K> IntegerReader<'env, K>
-where
-    K: PrimitiveInt,
-{
-    pub(crate) fn new(reader: Reader<Key<K>>) -> IntegerReader<K> {
-        IntegerReader {
-            inner: reader,
-        }
-    }
-
-    pub fn get(&self, store: IntegerStore, k: K) -> Result<Option<Value>, StoreError> {
-        self.inner.get(store.0, Key::new(k)?)
-    }
-
-    pub fn abort(self) {
-        self.inner.abort();
-    }
-}
-
-pub struct IntegerWriter<'env, K>
-where
-    K: PrimitiveInt,
-{
-    inner: Writer<'env, Key<K>>,
-}
-
-impl<'env, K> IntegerWriter<'env, K>
-where
-    K: PrimitiveInt,
-{
-    pub(crate) fn new(writer: Writer<Key<K>>) -> IntegerWriter<K> {
-        IntegerWriter {
-            inner: writer,
-        }
-    }
-
-    pub fn get(&self, store: IntegerStore, k: K) -> Result<Option<Value>, StoreError> {
-        self.inner.get(store.0, Key::new(k)?)
-    }
-
-    pub fn put(&mut self, store: IntegerStore, k: K, v: &Value) -> Result<(), StoreError> {
-        self.inner.put(store.0, Key::new(k)?, v)
-    }
-
-    fn abort(self) {
-        self.inner.abort();
-    }
-
-    fn commit(self) -> Result<(), StoreError> {
-        self.inner.commit()
-    }
-}
-
-#[derive(Copy, Clone)]
-pub struct IntegerStore(Store);
-
-impl IntegerStore {
-    pub fn new(db: Database) -> IntegerStore {
-        IntegerStore(Store::new(db))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    extern crate tempfile;
-
-    use self::tempfile::Builder;
-    use std::fs;
-
-    use super::*;
-    use *;
-
-    #[test]
-    fn test_integer_keys() {
-        let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
-        fs::create_dir_all(root.path()).expect("dir created");
-        let k = Rkv::new(root.path()).expect("new succeeded");
-        let s = k.open_or_create_integer("s").expect("open");
-
-        let mut writer = k.write_int::<u32>().expect("writer");
-
-        writer.put(s, 123, &Value::Str("hello!")).expect("write");
-        assert_eq!(writer.get(s, 123).expect("read"), Some(Value::Str("hello!")));
-        writer.commit().expect("committed");
-
-        let reader = k.read_int::<u32>().expect("reader");
-        assert_eq!(reader.get(s, 123).expect("read"), Some(Value::Str("hello!")));
-    }
-}
--- a/third_party/rust/rkv/src/lib.rs
+++ b/third_party/rust/rkv/src/lib.rs
@@ -1,52 +1,51 @@
-// Copyright 2018 Mozilla
+// 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 simple, humane, typed Rust interface to [LMDB](http://www.lmdb.tech/doc/)
 //!
 //! It aims to achieve the following:
 //!
 //! - Avoid LMDB's sharp edges (e.g., obscure error codes for common situations).
 //! - Report errors via [failure](https://docs.rs/failure/).
 //! - Correctly restrict access to one handle per process via a [Manager](struct.Manager.html).
-//! - Use Rust's type system to make single-typed key stores (including LMDB's own integer-keyed stores) safe and ergonomic.
+//! - Use Rust's type system to make single-typed key stores (including LMDB's own integer-keyed stores)
+//!   safe and ergonomic.
 //! - Encode and decode values via [bincode](https://docs.rs/bincode/)/[serde](https://docs.rs/serde/)
 //!   and type tags, achieving platform-independent storage and input/output flexibility.
 //!
 //! It exposes these primary abstractions:
 //!
 //! - [Manager](struct.Manager.html): a singleton that controls access to LMDB environments
-//! - [Rkv](struct.Rkv.html): an LMDB environment, which contains a set of key/value databases
-//! - [Store](struct.Store.html): an LMDB database, which contains a set of key/value pairs
+//! - [Rkv](struct.Rkv.html): an LMDB environment that contains a set of key/value databases
+//! - [SingleStore](store/single/struct.SingleStore.html): an LMDB database that contains a set of key/value pairs
 //!
-//! Keys can be anything that implements `AsRef<[u8]>` or integers (when accessing an [IntegerStore](struct.IntegerStore.html)).
+//! Keys can be anything that implements `AsRef<[u8]>` or integers
+//! (when accessing an [IntegerStore](store/integer/struct.IntegerStore.html)).
 //! Values can be any of the types defined by the [Value](value/enum.Value.html) enum, including:
 //!
 //! - booleans (`Value::Bool`)
 //! - integers (`Value::I64`, `Value::U64`)
 //! - floats (`Value::F64`)
 //! - strings (`Value::Str`)
 //! - blobs (`Value::Blob`)
 //!
 //! See [Value](value/enum.Value.html) for the complete list of supported types.
 //!
 //! ## Basic Usage
 //! ```
-//! extern crate rkv;
-//! extern crate tempfile;
-//!
-//! use rkv::{Manager, Rkv, Store, Value};
+//! use rkv::{Manager, Rkv, SingleStore, Value, StoreOptions};
 //! use std::fs;
 //! use tempfile::Builder;
 //!
 //! // First determine the path to the environment, which is represented
 //! // on disk as a directory containing two files:
 //! //
 //! //   * a data file containing the key/value stores
 //! //   * a lock file containing metadata about current transactions
@@ -54,171 +53,181 @@
 //! // In this example, we use the `tempfile` crate to create the directory.
 //! //
 //! let root = Builder::new().prefix("simple-db").tempdir().unwrap();
 //! fs::create_dir_all(root.path()).unwrap();
 //! let path = root.path();
 //!
 //! // The Manager enforces that each process opens the same environment
 //! // at most once by caching a handle to each environment that it opens.
-//! // Retrieve the handle to an opened environment—or create one if it hasn't
-//! // already been opened—by calling `Manager.get_or_create()`, passing it
-//! // an `Rkv` method that opens an environment (`Rkv::new` in this case):
+//! // Use it to retrieve the handle to an opened environment—or create one
+//! // if it hasn't already been opened:
 //! let created_arc = Manager::singleton().write().unwrap().get_or_create(path, Rkv::new).unwrap();
 //! let env = created_arc.read().unwrap();
 //!
-//! // Call `Rkv.open_or_create_default()` to get a handle to the default
-//! // (unnamed) store for the environment.
-//! let store: Store = env.open_or_create_default().unwrap();
+//! // Then you can use the environment handle to get a handle to a datastore:
+//! let store: SingleStore = env.open_single("mydb", StoreOptions::create()).unwrap();
 //!
 //! {
-//!     // Use a write transaction to mutate the store by calling
-//!     // `Rkv.write()` to create a `Writer`.  There can be only one
-//!     // writer for a given store; opening a second one will block
-//!     // until the first completes.
+//!     // Use a write transaction to mutate the store via a `Writer`.
+//!     // There can be only one writer for a given environment, so opening
+//!     // a second one will block until the first completes.
 //!     let mut writer = env.write().unwrap();
 //!
-//!     // Writer takes a `Store` reference as the first argument.
 //!     // Keys are `AsRef<[u8]>`, while values are `Value` enum instances.
 //!     // Use the `Blob` variant to store arbitrary collections of bytes.
-//!     writer.put(store, "int", &Value::I64(1234)).unwrap();
-//!     writer.put(store, "uint", &Value::U64(1234_u64)).unwrap();
-//!     writer.put(store, "float", &Value::F64(1234.0.into())).unwrap();
-//!     writer.put(store, "instant", &Value::Instant(1528318073700)).unwrap();
-//!     writer.put(store, "boolean", &Value::Bool(true)).unwrap();
-//!     writer.put(store, "string", &Value::Str("héllo, yöu")).unwrap();
-//!     writer.put(store, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
-//!     writer.put(store, "blob", &Value::Blob(b"blob")).unwrap();
+//!     // Putting data returns a `Result<(), StoreError>`, where StoreError
+//!     // is an enum identifying the reason for a failure.
+//!     store.put(&mut writer, "int", &Value::I64(1234)).unwrap();
+//!     store.put(&mut writer, "uint", &Value::U64(1234_u64)).unwrap();
+//!     store.put(&mut writer, "float", &Value::F64(1234.0.into())).unwrap();
+//!     store.put(&mut writer, "instant", &Value::Instant(1528318073700)).unwrap();
+//!     store.put(&mut writer, "boolean", &Value::Bool(true)).unwrap();
+//!     store.put(&mut writer, "string", &Value::Str("Héllo, wörld!")).unwrap();
+//!     store.put(&mut writer, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
+//!     store.put(&mut writer, "blob", &Value::Blob(b"blob")).unwrap();
 //!
 //!     // You must commit a write transaction before the writer goes out
 //!     // of scope, or the transaction will abort and the data won't persist.
 //!     writer.commit().unwrap();
 //! }
 //!
 //! {
-//!     // Use a read transaction to query the store by calling `Rkv.read()`
-//!     // to create a `Reader`.  There can be unlimited concurrent readers
-//!     // for a store, and readers never block on a writer nor other readers.
+//!     // Use a read transaction to query the store via a `Reader`.
+//!     // There can be multiple concurrent readers for a store, and readers
+//!     // never block on a writer nor other readers.
 //!     let reader = env.read().expect("reader");
 //!
-//!     // To retrieve data, call `Reader.get()`, passing it the target store
-//!     // and the key for the value to retrieve.
-//!     println!("Get int {:?}", reader.get(store, "int").unwrap());
-//!     println!("Get uint {:?}", reader.get(store, "uint").unwrap());
-//!     println!("Get float {:?}", reader.get(store, "float").unwrap());
-//!     println!("Get instant {:?}", reader.get(store, "instant").unwrap());
-//!     println!("Get boolean {:?}", reader.get(store, "boolean").unwrap());
-//!     println!("Get string {:?}", reader.get(store, "string").unwrap());
-//!     println!("Get json {:?}", reader.get(store, "json").unwrap());
-//!     println!("Get blob {:?}", reader.get(store, "blob").unwrap());
+//!     // Keys are `AsRef<u8>`, and the return value is `Result<Option<Value>, StoreError>`.
+//!     println!("Get int {:?}", store.get(&reader, "int").unwrap());
+//!     println!("Get uint {:?}", store.get(&reader, "uint").unwrap());
+//!     println!("Get float {:?}", store.get(&reader, "float").unwrap());
+//!     println!("Get instant {:?}", store.get(&reader, "instant").unwrap());
+//!     println!("Get boolean {:?}", store.get(&reader, "boolean").unwrap());
+//!     println!("Get string {:?}", store.get(&reader, "string").unwrap());
+//!     println!("Get json {:?}", store.get(&reader, "json").unwrap());
+//!     println!("Get blob {:?}", store.get(&reader, "blob").unwrap());
 //!
 //!     // Retrieving a non-existent value returns `Ok(None)`.
-//!     println!("Get non-existent value {:?}", reader.get(store, "non-existent"));
+//!     println!("Get non-existent value {:?}", store.get(&reader, "non-existent").unwrap());
 //!
 //!     // A read transaction will automatically close once the reader
 //!     // goes out of scope, so isn't necessary to close it explicitly,
 //!     // although you can do so by calling `Reader.abort()`.
 //! }
 //!
 //! {
 //!     // Aborting a write transaction rolls back the change(s).
 //!     let mut writer = env.write().unwrap();
-//!     writer.put(store, "foo", &Value::Str("bar")).unwrap();
+//!     store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
 //!     writer.abort();
-//!
 //!     let reader = env.read().expect("reader");
-//!     println!("It should be None! ({:?})", reader.get(store, "foo").unwrap());
+//!     println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
 //! }
 //!
 //! {
 //!     // Explicitly aborting a transaction is not required unless an early
 //!     // abort is desired, since both read and write transactions will
 //!     // implicitly be aborted once they go out of scope.
 //!     {
 //!         let mut writer = env.write().unwrap();
-//!         writer.put(store, "foo", &Value::Str("bar")).unwrap();
+//!         store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
 //!     }
 //!     let reader = env.read().expect("reader");
-//!     println!("It should be None! ({:?})", reader.get(store, "foo").unwrap());
+//!     println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
 //! }
 //!
 //! {
 //!     // Deleting a key/value pair also requires a write transaction.
 //!     let mut writer = env.write().unwrap();
-//!     writer.put(store, "foo", &Value::Str("bar")).unwrap();
-//!     writer.put(store, "bar", &Value::Str("baz")).unwrap();
-//!     writer.delete(store, "foo").unwrap();
+//!     store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
+//!     store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
+//!     store.delete(&mut writer, "foo").unwrap();
 //!
-//!     // A write transaction also supports reading, the version of the
-//!     // store that it reads includes changes it has made regardless of
+//!     // A write transaction also supports reading, and the version of the
+//!     // store that it reads includes the changes it has made regardless of
 //!     // the commit state of that transaction.
+
 //!     // In the code above, "foo" and "bar" were put into the store,
-//!     // then "foo" was deleted so only "bar" will return a result.
-//!     println!("It should be None! ({:?})", writer.get(store, "foo").unwrap());
-//!     println!("Get bar ({:?})", writer.get(store, "bar").unwrap());
+//!     // then "foo" was deleted so only "bar" will return a result when the
+//!     // database is queried via the writer.
+//!     println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
+//!     println!("Get bar ({:?})", store.get(&writer, "bar").unwrap());
+//!
+//!     // But a reader won't see that change until the write transaction
+//!     // is committed.
+//!     {
+//!         let reader = env.read().expect("reader");
+//!         println!("Get foo {:?}", store.get(&reader, "foo").unwrap());
+//!         println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
+//!     }
 //!     writer.commit().unwrap();
-//!     let reader = env.read().expect("reader");
-//!     println!("It should be None! ({:?})", reader.get(store, "foo").unwrap());
-//!     println!("Get bar {:?}", reader.get(store, "bar").unwrap());
+//!     {
+//!         let reader = env.read().expect("reader");
+//!         println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
+//!         println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
+//!     }
 //!
 //!     // Committing a transaction consumes the writer, preventing you
 //!     // from reusing it by failing at compile time with an error.
-//!     // This line would report error[E0382]: use of moved value: `writer`.
-//!     // writer.put(store, "baz", &Value::Str("buz")).unwrap();
+//!     // This line would report error[E0382]: borrow of moved value: `writer`.
+//!     // store.put(&mut writer, "baz", &Value::Str("buz")).unwrap();
 //! }
 //! ```
 
 #![allow(dead_code)]
 
-#[macro_use]
-extern crate arrayref;
-#[macro_use]
-extern crate failure;
-#[macro_use]
-extern crate lazy_static;
-
-extern crate bincode;
-extern crate lmdb;
-extern crate ordered_float;
-extern crate serde; // So we can specify trait bounds. Everything else is bincode.
-extern crate url;
-extern crate uuid;
-
 pub use lmdb::{
     DatabaseFlags,
     EnvironmentBuilder,
     EnvironmentFlags,
     WriteFlags,
 };
 
 mod env;
 pub mod error;
-mod integer;
 mod manager;
 mod readwrite;
+pub mod store;
 pub mod value;
 
-pub use env::Rkv;
+pub use lmdb::{
+    Cursor,
+    Database,
+    Iter as LmdbIter,
+    RoCursor,
+    Stat,
+};
 
-pub use error::{
+pub use self::readwrite::{
+    Reader,
+    Writer,
+};
+pub use self::store::integer::{
+    IntegerStore,
+    PrimitiveInt,
+};
+pub use self::store::integermulti::MultiIntegerStore;
+pub use self::store::multi::MultiStore;
+pub use self::store::single::SingleStore;
+pub use self::store::Options as StoreOptions;
+
+pub use self::env::Rkv;
+
+pub use self::error::{
     DataError,
     StoreError,
 };
 
-pub use integer::{
-    IntegerReader,
-    IntegerStore,
-    IntegerWriter,
-    PrimitiveInt,
-};
+pub use self::manager::Manager;
 
-pub use manager::Manager;
-
-pub use readwrite::{
-    Reader,
-    Store,
-    Writer,
-};
-
-pub use value::{
+pub use self::value::{
     OwnedValue,
     Value,
 };
+
+fn read_transform(val: Result<&[u8], lmdb::Error>) -> Result<Option<Value>, StoreError> {
+    match val {
+        Ok(bytes) => Value::from_tagged_slice(bytes).map(Some).map_err(StoreError::DataError),
+        Err(lmdb::Error::NotFound) => Ok(None),
+        Err(e) => Err(StoreError::LmdbError(e)),
+    }
+}
--- a/third_party/rust/rkv/src/manager.rs
+++ b/third_party/rust/rkv/src/manager.rs
@@ -1,18 +1,19 @@
-// Copyright 2018 Mozilla
+// 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.
 
+use lazy_static::lazy_static;
 use std::collections::BTreeMap;
 
 use std::io::{
     self,
     Error,
     ErrorKind,
 };
 
@@ -27,19 +28,19 @@ use std::path::{
 
 use std::sync::{
     Arc,
     RwLock,
 };
 
 use url::Url;
 
-use error::StoreError;
+use crate::error::StoreError;
 
-use Rkv;
+use crate::Rkv;
 
 /// 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.
 lazy_static! {
     static ref MANAGER: RwLock<Manager> = RwLock::new(Manager::new());
 }
 
 // Workaround the UNC path on Windows, see https://github.com/rust-lang/rust/issues/42869.
@@ -116,20 +117,18 @@ impl Manager {
                 e.insert(k).clone()
             },
         })
     }
 }
 
 #[cfg(test)]
 mod tests {
-    extern crate tempfile;
-
-    use self::tempfile::Builder;
     use std::fs;
+    use tempfile::Builder;
 
     use super::*;
 
     /// Test that the manager will return the same Rkv instance each time for each path.
     #[test]
     fn test_same() {
         let root = Builder::new().prefix("test_same").tempdir().expect("tempdir");
         fs::create_dir_all(root.path()).expect("dir created");
--- a/third_party/rust/rkv/src/readwrite.rs
+++ b/third_party/rust/rkv/src/readwrite.rs
@@ -1,174 +1,91 @@
-// Copyright 2018 Mozilla
+// 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.
 
-use lmdb;
-
-use std::marker::PhantomData;
-
 use lmdb::{
-    Cursor,
     Database,
-    Iter as LmdbIter,
     RoCursor,
     RoTransaction,
     RwTransaction,
     Transaction,
+    WriteFlags,
 };
 
-use lmdb::WriteFlags;
+use crate::error::StoreError;
+use crate::read_transform;
+use crate::value::Value;
 
-use error::StoreError;
-
-use value::Value;
+pub struct Reader<'env>(pub RoTransaction<'env>);
+pub struct Writer<'env>(pub RwTransaction<'env>);
 
-fn read_transform(val: Result<&[u8], lmdb::Error>) -> Result<Option<Value>, StoreError> {
-    match val {
-        Ok(bytes) => Value::from_tagged_slice(bytes).map(Some).map_err(StoreError::DataError),
-        Err(lmdb::Error::NotFound) => Ok(None),
-        Err(e) => Err(StoreError::LmdbError(e)),
+pub trait Readable {
+    fn get<K: AsRef<[u8]>>(&self, db: Database, k: &K) -> Result<Option<Value>, StoreError>;
+    fn open_ro_cursor(&self, db: Database) -> Result<RoCursor, StoreError>;
+}
+
+impl<'env> Readable for Reader<'env> {
+    fn get<K: AsRef<[u8]>>(&self, db: Database, k: &K) -> Result<Option<Value>, StoreError> {
+        let bytes = self.0.get(db, &k);
+        read_transform(bytes)
+    }
+
+    fn open_ro_cursor(&self, db: Database) -> Result<RoCursor, StoreError> {
+        self.0.open_ro_cursor(db).map_err(StoreError::LmdbError)
     }
 }
 
-pub struct Writer<'env, K>
-where
-    K: AsRef<[u8]>,
-{
-    tx: RwTransaction<'env>,
-    phantom: PhantomData<K>,
-}
+impl<'env> Reader<'env> {
+    pub(crate) fn new(txn: RoTransaction) -> Reader {
+        Reader(txn)
+    }
 
-pub struct Reader<'env, K>
-where
-    K: AsRef<[u8]>,
-{
-    tx: RoTransaction<'env>,
-    phantom: PhantomData<K>,
+    pub fn abort(self) {
+        self.0.abort();
+    }
 }
 
-pub struct Iter<'env> {
-    iter: LmdbIter<'env>,
-    cursor: RoCursor<'env>,
-}
-
-impl<'env, K> Writer<'env, K>
-where
-    K: AsRef<[u8]>,
-{
-    pub(crate) fn new(txn: RwTransaction) -> Writer<K> {
-        Writer {
-            tx: txn,
-            phantom: PhantomData,
-        }
-    }
-
-    pub fn get(&self, store: Store, k: K) -> Result<Option<Value>, StoreError> {
-        let bytes = self.tx.get(store.0, &k);
+impl<'env> Readable for Writer<'env> {
+    fn get<K: AsRef<[u8]>>(&self, db: Database, k: &K) -> Result<Option<Value>, StoreError> {
+        let bytes = self.0.get(db, &k);
         read_transform(bytes)
     }
 
-    // TODO: flags
-    pub fn put(&mut self, store: Store, k: K, v: &Value) -> Result<(), StoreError> {
-        // TODO: don't allocate twice.
-        let bytes = v.to_bytes()?;
-        self.tx.put(store.0, &k, &bytes, WriteFlags::empty()).map_err(StoreError::LmdbError)
+    fn open_ro_cursor(&self, db: Database) -> Result<RoCursor, StoreError> {
+        self.0.open_ro_cursor(db).map_err(StoreError::LmdbError)
     }
+}
 
-    pub fn delete(&mut self, store: Store, k: K) -> Result<(), StoreError> {
-        self.tx.del(store.0, &k, None).map_err(StoreError::LmdbError)
-    }
-
-    pub fn delete_value(&mut self, _store: Store, _k: K, _v: &Value) -> Result<(), StoreError> {
-        // Even better would be to make this a method only on a dupsort store —
-        // it would need a little bit of reorganizing of types and traits,
-        // but when I see "If the database does not support sorted duplicate
-        // data items (MDB_DUPSORT) the data parameter is ignored" in the docs,
-        // I see a footgun that we can avoid by using the type system.
-        unimplemented!();
+impl<'env> Writer<'env> {
+    pub(crate) fn new(txn: RwTransaction) -> Writer {
+        Writer(txn)
     }
 
     pub fn commit(self) -> Result<(), StoreError> {
-        self.tx.commit().map_err(StoreError::LmdbError)
-    }
-
-    pub fn abort(self) {
-        self.tx.abort();
-    }
-}
-
-impl<'env, K> Reader<'env, K>
-where
-    K: AsRef<[u8]>,
-{
-    pub(crate) fn new(txn: RoTransaction) -> Reader<K> {
-        Reader {
-            tx: txn,
-            phantom: PhantomData,
-        }
-    }
-
-    pub fn get(&self, store: Store, k: K) -> Result<Option<Value>, StoreError> {
-        let bytes = self.tx.get(store.0, &k);
-        read_transform(bytes)
+        self.0.commit().map_err(StoreError::LmdbError)
     }
 
     pub fn abort(self) {
-        self.tx.abort();
-    }
-
-    pub fn iter_start(&self, store: Store) -> Result<Iter, StoreError> {
-        let mut cursor = self.tx.open_ro_cursor(store.0).map_err(StoreError::LmdbError)?;
-
-        // We call Cursor.iter() instead of Cursor.iter_start() because
-        // the latter panics at "called `Result::unwrap()` on an `Err` value:
-        // NotFound" when there are no items in the store, whereas the former
-        // returns an iterator that yields no items.
-        //
-        // And since we create the Cursor and don't change its position, we can
-        // be sure that a call to Cursor.iter() will start at the beginning.
-        //
-        let iter = cursor.iter();
-
-        Ok(Iter {
-            iter,
-            cursor,
-        })
+        self.0.abort();
     }
 
-    pub fn iter_from(&self, store: Store, k: K) -> Result<Iter, StoreError> {
-        let mut cursor = self.tx.open_ro_cursor(store.0).map_err(StoreError::LmdbError)?;
-        let iter = cursor.iter_from(k);
-        Ok(Iter {
-            iter,
-            cursor,
-        })
+    pub(crate) fn put<K: AsRef<[u8]>>(
+        &mut self,
+        db: Database,
+        k: &K,
+        v: &Value,
+        flags: WriteFlags,
+    ) -> Result<(), StoreError> {
+        // TODO: don't allocate twice.
+        self.0.put(db, &k, &v.to_bytes()?, flags).map_err(StoreError::LmdbError)
+    }
+
+    pub(crate) fn delete<K: AsRef<[u8]>>(&mut self, db: Database, k: &K, v: Option<&[u8]>) -> Result<(), StoreError> {
+        self.0.del(db, &k, v).map_err(StoreError::LmdbError)
     }
 }
-
-impl<'env> Iterator for Iter<'env> {
-    type Item = (&'env [u8], Result<Option<Value<'env>>, StoreError>);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.iter.next() {
-            None => None,
-            Some((key, bytes)) => Some((key, read_transform(Ok(bytes)))),
-        }
-    }
-}
-
-/// New type around an `lmdb::Database`.  At this time, the underlying LMDB
-/// handle (within lmdb-rs::Database) is a C integer, so Copy is automatic.
-#[derive(Copy, Clone)]
-pub struct Store(Database);
-
-impl Store {
-    pub fn new(db: Database) -> Store {
-        Store(db)
-    }
-}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/src/store.rs
@@ -0,0 +1,21 @@
+pub mod integer;
+pub mod integermulti;
+pub mod multi;
+pub mod single;
+
+use lmdb::DatabaseFlags;
+
+#[derive(Default, Debug, Copy, Clone)]
+pub struct Options {
+    pub create: bool,
+    pub flags: DatabaseFlags,
+}
+
+impl Options {
+    pub fn create() -> Options {
+        Options {
+            create: true,
+            flags: DatabaseFlags::empty(),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/src/store/integer.rs
@@ -0,0 +1,141 @@
+// 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.
+
+use std::marker::PhantomData;
+
+use bincode::serialize;
+
+use serde::Serialize;
+
+use lmdb::Database;
+
+use crate::error::{
+    DataError,
+    StoreError,
+};
+
+use crate::readwrite::{
+    Readable,
+    Writer,
+};
+
+use crate::value::Value;
+
+use crate::store::single::SingleStore;
+
+pub trait EncodableKey {
+    fn to_bytes(&self) -> Result<Vec<u8>, DataError>;
+}
+
+pub trait PrimitiveInt: EncodableKey {}
+
+impl PrimitiveInt for u32 {}
+
+impl<T> EncodableKey for T
+where
+    T: Serialize,
+{
+    fn to_bytes(&self) -> Result<Vec<u8>, DataError> {
+        serialize(self) // TODO: limited key length.
+            .map_err(|e| e.into())
+    }
+}
+
+pub(crate) struct Key<K> {
+    bytes: Vec<u8>,
+    phantom: PhantomData<K>,
+}
+
+impl<K> AsRef<[u8]> for Key<K>
+where
+    K: EncodableKey,
+{
+    fn as_ref(&self) -> &[u8] {
+        self.bytes.as_ref()
+    }
+}
+
+impl<K> Key<K>
+where
+    K: EncodableKey,
+{
+    #[allow(clippy::new_ret_no_self)]
+    pub(crate) fn new(k: &K) -> Result<Key<K>, DataError> {
+        Ok(Key {
+            bytes: k.to_bytes()?,
+            phantom: PhantomData,
+        })
+    }
+}
+
+pub struct IntegerStore<K>
+where
+    K: PrimitiveInt,
+{
+    inner: SingleStore,
+    phantom: PhantomData<K>,
+}
+
+impl<K> IntegerStore<K>
+where
+    K: PrimitiveInt,
+{
+    pub(crate) fn new(db: Database) -> IntegerStore<K> {
+        IntegerStore {
+            inner: SingleStore::new(db),
+            phantom: PhantomData,
+        }
+    }
+
+    pub fn get<'env, T: Readable>(&self, reader: &'env T, k: K) -> Result<Option<Value<'env>>, StoreError> {
+        self.inner.get(reader, Key::new(&k)?)
+    }
+
+    pub fn put(&self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
+        self.inner.put(writer, Key::new(&k)?, v)
+    }
+
+    pub fn delete(&self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
+        self.inner.delete(writer, Key::new(&k)?)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::fs;
+    use tempfile::Builder;
+
+    use super::*;
+    use crate::*;
+
+    #[test]
+    fn test_integer_keys() {
+        let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
+        fs::create_dir_all(root.path()).expect("dir created");
+        let k = Rkv::new(root.path()).expect("new succeeded");
+        let s = k.open_integer("s", StoreOptions::create()).expect("open");
+
+        macro_rules! test_integer_keys {
+            ($type:ty, $key:expr) => {{
+                let mut writer = k.write().expect("writer");
+
+                s.put(&mut writer, $key, &Value::Str("hello!")).expect("write");
+                assert_eq!(s.get(&writer, $key).expect("read"), Some(Value::Str("hello!")));
+                writer.commit().expect("committed");
+
+                let reader = k.read().expect("reader");
+                assert_eq!(s.get(&reader, $key).expect("read"), Some(Value::Str("hello!")));
+            }};
+        }
+
+        test_integer_keys!(u32, std::u32::MIN);
+        test_integer_keys!(u32, std::u32::MAX);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/src/store/integermulti.rs
@@ -0,0 +1,114 @@
+// Copyright 2018 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.
+
+use lmdb::{
+    Database,
+    WriteFlags,
+};
+
+use std::marker::PhantomData;
+
+use crate::error::StoreError;
+
+use crate::readwrite::{
+    Readable,
+    Writer,
+};
+
+use crate::value::Value;
+
+use crate::store::multi::{
+    Iter,
+    MultiStore,
+};
+
+use crate::store::integer::{
+    Key,
+    PrimitiveInt,
+};
+
+pub struct MultiIntegerStore<K>
+where
+    K: PrimitiveInt,
+{
+    inner: MultiStore,
+    phantom: PhantomData<K>,
+}
+
+impl<K> MultiIntegerStore<K>
+where
+    K: PrimitiveInt,
+{
+    pub(crate) fn new(db: Database) -> MultiIntegerStore<K> {
+        MultiIntegerStore {
+            inner: MultiStore::new(db),
+            phantom: PhantomData,
+        }
+    }
+
+    pub fn get<'env, T: Readable>(&self, reader: &'env T, k: K) -> Result<Iter<'env>, StoreError> {
+        self.inner.get(reader, Key::new(&k)?)
+    }
+
+    pub fn get_first<'env, T: Readable>(&self, reader: &'env T, k: K) -> Result<Option<Value<'env>>, StoreError> {
+        self.inner.get_first(reader, Key::new(&k)?)
+    }
+
+    pub fn put(&self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
+        self.inner.put(writer, Key::new(&k)?, v)
+    }
+
+    pub fn put_with_flags(&self, writer: &mut Writer, k: K, v: &Value, flags: WriteFlags) -> Result<(), StoreError> {
+        self.inner.put_with_flags(writer, Key::new(&k)?, v, flags)
+    }
+
+    pub fn delete_all(&self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
+        self.inner.delete_all(writer, Key::new(&k)?)
+    }
+
+    pub fn delete(&self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
+        self.inner.delete(writer, Key::new(&k)?, v)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    extern crate tempfile;
+
+    use self::tempfile::Builder;
+    use std::fs;
+
+    use super::*;
+    use crate::*;
+
+    #[test]
+    fn test_integer_keys() {
+        let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
+        fs::create_dir_all(root.path()).expect("dir created");
+        let k = Rkv::new(root.path()).expect("new succeeded");
+        let s = k.open_multi_integer("s", StoreOptions::create()).expect("open");
+
+        macro_rules! test_integer_keys {
+            ($type:ty, $key:expr) => {{
+                let mut writer = k.write().expect("writer");
+
+                s.put(&mut writer, $key, &Value::Str("hello!")).expect("write");
+                assert_eq!(s.get_first(&writer, $key).expect("read"), Some(Value::Str("hello!")));
+                writer.commit().expect("committed");
+
+                let reader = k.read().expect("reader");
+                assert_eq!(s.get_first(&reader, $key).expect("read"), Some(Value::Str("hello!")));
+            }};
+        }
+
+        test_integer_keys!(u32, std::u32::MIN);
+        test_integer_keys!(u32, std::u32::MAX);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/src/store/multi.rs
@@ -0,0 +1,147 @@
+// Copyright 2018 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.
+
+use crate::{
+    error::StoreError,
+    read_transform,
+    readwrite::{
+        Readable,
+        Writer,
+    },
+    value::Value,
+};
+use lmdb::{
+    Cursor,
+    Database,
+    Iter as LmdbIter,
+    //    IterDup as LmdbIterDup,
+    RoCursor,
+    WriteFlags,
+};
+
+#[derive(Copy, Clone)]
+pub struct MultiStore {
+    db: Database,
+}
+
+pub struct Iter<'env> {
+    iter: LmdbIter<'env>,
+    cursor: RoCursor<'env>,
+}
+
+impl MultiStore {
+    pub(crate) fn new(db: Database) -> MultiStore {
+        MultiStore {
+            db,
+        }
+    }
+
+    /// Provides a cursor to all of the values for the duplicate entries that match this key
+    pub fn get<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Iter, StoreError> {
+        let mut cursor = reader.open_ro_cursor(self.db)?;
+        let iter = cursor.iter_dup_of(k);
+        Ok(Iter {
+            iter,
+            cursor,
+        })
+    }
+
+    /// Provides the first value that matches this key
+    pub fn get_first<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Option<Value>, StoreError> {
+        reader.get(self.db, &k)
+    }
+
+    /// Insert a value at the specified key.
+    /// This put will allow duplicate entries.  If you wish to have duplicate entries
+    /// rejected, use the `put_with_flags` function and specify NO_DUP_DATA
+    pub fn put<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
+        writer.put(self.db, &k, v, WriteFlags::empty())
+    }
+
+    pub fn put_with_flags<K: AsRef<[u8]>>(
+        self,
+        writer: &mut Writer,
+        k: K,
+        v: &Value,
+        flags: WriteFlags,
+    ) -> Result<(), StoreError> {
+        writer.put(self.db, &k, v, flags)
+    }
+
+    pub fn delete_all<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
+        writer.delete(self.db, &k, None)
+    }
+
+    pub fn delete<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
+        writer.delete(self.db, &k, Some(&v.to_bytes()?))
+    }
+
+    /* TODO - Figure out how to solve the need to have the cursor stick around when
+     *        we are producing iterators from MultiIter
+    /// Provides an iterator starting at the lexographically smallest value in the store
+    pub fn iter_start(&self, store: MultiStore) -> Result<MultiIter, StoreError> {
+        let mut cursor = self.tx.open_ro_cursor(store.0).map_err(StoreError::LmdbError)?;
+
+        // We call Cursor.iter() instead of Cursor.iter_start() because
+        // the latter panics at "called `Result::unwrap()` on an `Err` value:
+        // NotFound" when there are no items in the store, whereas the former
+        // returns an iterator that yields no items.
+        //
+        // And since we create the Cursor and don't change its position, we can
+        // be sure that a call to Cursor.iter() will start at the beginning.
+        //
+        let iter = cursor.iter_dup();
+
+        Ok(MultiIter {
+            iter,
+            cursor,
+        })
+    }
+    */
+}
+
+/*
+fn read_transform_owned(val: Result<&[u8], lmdb::Error>) -> Result<Option<OwnedValue>, StoreError> {
+    match val {
+        Ok(bytes) => Value::from_tagged_slice(bytes).map(|v| Some(OwnedValue::from(&v))).map_err(StoreError::DataError),
+        Err(lmdb::Error::NotFound) => Ok(None),
+        Err(e) => Err(StoreError::LmdbError(e)),
+    }
+}
+
+impl<'env> Iterator for MultiIter<'env> {
+    type Item = Iter<'env>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.iter.next() {
+            None => None,
+            Some(iter) => Some(Iter {
+                iter,
+                cursor,
+            }),
+        }
+    }
+}
+*/
+
+impl<'env> Iterator for Iter<'env> {
+    type Item = Result<(&'env [u8], Option<Value<'env>>), StoreError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.iter.next() {
+            None => None,
+            Some(Ok((key, bytes))) => match read_transform(Ok(bytes)) {
+                Ok(val) => Some(Ok((key, val))),
+                Err(err) => Some(Err(err)),
+            },
+            Some(Err(err)) => Some(Err(StoreError::LmdbError(err))),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/src/store/single.rs
@@ -0,0 +1,100 @@
+// Copyright 2018 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.
+
+use crate::{
+    error::StoreError,
+    read_transform,
+    readwrite::{
+        Readable,
+        Writer,
+    },
+    value::Value,
+};
+use lmdb::{
+    Cursor,
+    Database,
+    Iter as LmdbIter,
+    RoCursor,
+    WriteFlags,
+};
+
+#[derive(Copy, Clone)]
+pub struct SingleStore {
+    db: Database,
+}
+
+pub struct Iter<'env> {
+    iter: LmdbIter<'env>,
+    cursor: RoCursor<'env>,
+}
+
+impl SingleStore {
+    pub(crate) fn new(db: Database) -> SingleStore {
+        SingleStore {
+            db,
+        }
+    }
+
+    pub fn get<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Option<Value>, StoreError> {
+        reader.get(self.db, &k)
+    }
+
+    // TODO: flags
+    pub fn put<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
+        writer.put(self.db, &k, v, WriteFlags::empty())
+    }
+
+    pub fn delete<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
+        writer.delete(self.db, &k, None)
+    }
+
+    pub fn iter_start<T: Readable>(self, reader: &T) -> Result<Iter, StoreError> {
+        let mut cursor = reader.open_ro_cursor(self.db)?;
+
+        // We call Cursor.iter() instead of Cursor.iter_start() because
+        // the latter panics at "called `Result::unwrap()` on an `Err` value:
+        // NotFound" when there are no items in the store, whereas the former
+        // returns an iterator that yields no items.
+        //
+        // And since we create the Cursor and don't change its position, we can
+        // be sure that a call to Cursor.iter() will start at the beginning.
+        //
+        let iter = cursor.iter();
+
+        Ok(Iter {
+            iter,
+            cursor,
+        })
+    }
+
+    pub fn iter_from<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Iter, StoreError> {
+        let mut cursor = reader.open_ro_cursor(self.db)?;
+        let iter = cursor.iter_from(k);
+        Ok(Iter {
+            iter,
+            cursor,
+        })
+    }
+}
+
+impl<'env> Iterator for Iter<'env> {
+    type Item = Result<(&'env [u8], Option<Value<'env>>), StoreError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.iter.next() {
+            None => None,
+            Some(Ok((key, bytes))) => match read_transform(Ok(bytes)) {
+                Ok(val) => Some(Ok((key, val))),
+                Err(err) => Some(Err(err)),
+            },
+            Some(Err(err)) => Some(Err(StoreError::LmdbError(err))),
+        }
+    }
+}
--- a/third_party/rust/rkv/src/value.rs
+++ b/third_party/rust/rkv/src/value.rs
@@ -1,31 +1,31 @@
-// Copyright 2018 Mozilla
+// 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.
 
-use ordered_float::OrderedFloat;
-
+use arrayref::array_ref;
 use bincode::{
     deserialize,
     serialize,
 };
+use ordered_float::OrderedFloat;
 
 use uuid::{
     Bytes,
     Uuid,
 };
 
-use error::DataError;
+use crate::error::DataError;
 
 /// We define a set of types, associated with simple integers, to annotate values
 /// stored in LMDB. This is to avoid an accidental 'cast' from a value of one type
 /// to another. For this reason we don't simply use `deserialize` from the `bincode`
 /// crate.
 #[repr(u8)]
 #[derive(Debug, PartialEq, Eq)]
 pub enum Type {
@@ -41,16 +41,17 @@ pub enum Type {
 }
 
 /// We use manual tagging, because <https://github.com/serde-rs/serde/issues/610>.
 impl Type {
     pub fn from_tag(tag: u8) -> Result<Type, DataError> {
         Type::from_primitive(tag).ok_or_else(|| DataError::UnknownType(tag))
     }
 
+    #[allow(clippy::wrong_self_convention)]
     pub fn to_tag(self) -> u8 {
         self as u8
     }
 
     fn from_primitive(p: u8) -> Option<Type> {
         match p {
             1 => Some(Type::Bool),
             2 => Some(Type::U64),
@@ -90,17 +91,17 @@ pub enum Value<'s> {
     F64(OrderedFloat<f64>),
     Instant(i64), // Millisecond-precision timestamp.
     Uuid(&'s Bytes),
     Str(&'s str),
     Json(&'s str),
     Blob(&'s [u8]),
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
 pub enum OwnedValue {
     Bool(bool),
     U64(u64),
     I64(i64),
     F64(f64),
     Instant(i64), // Millisecond-precision timestamp.
     Uuid(Uuid),
     Str(String),
@@ -153,17 +154,18 @@ impl<'s> Value<'s> {
             Type::Instant => deserialize(data).map(Value::Instant),
             Type::Str => deserialize(data).map(Value::Str),
             Type::Json => deserialize(data).map(Value::Json),
             Type::Blob => deserialize(data).map(Value::Blob),
             Type::Uuid => {
                 // Processed above to avoid verbose duplication of error transforms.
                 unreachable!()
             },
-        }.map_err(|e| DataError::DecodingError {
+        }
+        .map_err(|e| DataError::DecodingError {
             value_type: t,
             err: e,
         })
     }
 
     pub fn to_bytes(&self) -> Result<Vec<u8>, DataError> {
         match self {
             Value::Bool(ref v) => serialize(&(Type::Bool.to_tag(), *v)),
@@ -173,17 +175,18 @@ impl<'s> Value<'s> {
             Value::Instant(ref v) => serialize(&(Type::Instant.to_tag(), *v)),
             Value::Str(ref v) => serialize(&(Type::Str.to_tag(), v)),
             Value::Json(ref v) => serialize(&(Type::Json.to_tag(), v)),
             Value::Blob(ref v) => serialize(&(Type::Blob.to_tag(), v)),
             Value::Uuid(ref v) => {
                 // Processed above to avoid verbose duplication of error transforms.
                 serialize(&(Type::Uuid.to_tag(), v))
             },
-        }.map_err(DataError::EncodingError)
+        }
+        .map_err(DataError::EncodingError)
     }
 }
 
 impl<'s> From<&'s Value<'s>> for OwnedValue {
     fn from(value: &Value) -> OwnedValue {
         match value {
             Value::Bool(ref v) => OwnedValue::Bool(*v),
             Value::U64(ref v) => OwnedValue::U64(*v),
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/tests/integer-store.rs
@@ -0,0 +1,77 @@
+// 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.
+
+use rkv::{
+    PrimitiveInt,
+    Rkv,
+    StoreOptions,
+    Value,
+};
+use serde_derive::Serialize;
+use std::fs;
+use tempfile::Builder;
+
+#[test]
+fn test_integer_keys() {
+    let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
+    fs::create_dir_all(root.path()).expect("dir created");
+    let k = Rkv::new(root.path()).expect("new succeeded");
+    let s = k.open_integer("s", StoreOptions::create()).expect("open");
+
+    macro_rules! test_integer_keys {
+        ($store:expr, $key:expr) => {{
+            let mut writer = k.write().expect("writer");
+
+            $store.put(&mut writer, $key, &Value::Str("hello!")).expect("write");
+            assert_eq!($store.get(&writer, $key).expect("read"), Some(Value::Str("hello!")));
+            writer.commit().expect("committed");
+
+            let reader = k.read().expect("reader");
+            assert_eq!($store.get(&reader, $key).expect("read"), Some(Value::Str("hello!")));
+        }};
+    }
+
+    // The integer module provides only the u32 integer key variant
+    // of IntegerStore, so we can use it without further ado.
+    test_integer_keys!(s, std::u32::MIN);
+    test_integer_keys!(s, std::u32::MAX);
+
+    // If you want to use another integer key variant, you need to implement
+    // a newtype, implement PrimitiveInt, and implement or derive Serialize
+    // for it.  Here we do so for the i32 type.
+
+    // DANGER!  Doing this enables you to open a store with multiple,
+    // different integer key types, which may result in unexpected behavior.
+    // Make sure you know what you're doing!
+
+    let t = k.open_integer("s", StoreOptions::create()).expect("open");
+
+    #[derive(Serialize)]
+    struct I32(i32);
+    impl PrimitiveInt for I32 {}
+    test_integer_keys!(t, I32(std::i32::MIN));
+    test_integer_keys!(t, I32(std::i32::MAX));
+
+    let u = k.open_integer("s", StoreOptions::create()).expect("open");
+
+    #[derive(Serialize)]
+    struct U16(u16);
+    impl PrimitiveInt for U16 {}
+    test_integer_keys!(u, U16(std::u16::MIN));
+    test_integer_keys!(u, U16(std::u16::MAX));
+
+    let v = k.open_integer("s", StoreOptions::create()).expect("open");
+
+    #[derive(Serialize)]
+    struct U64(u64);
+    impl PrimitiveInt for U64 {}
+    test_integer_keys!(v, U64(std::u64::MIN));
+    test_integer_keys!(v, U64(std::u64::MAX));
+}
--- a/third_party/rust/rkv/tests/manager.rs
+++ b/third_party/rust/rkv/tests/manager.rs
@@ -1,31 +1,27 @@
-// Copyright 2018 Mozilla
+// 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;
-extern crate tempfile;
-
 use rkv::{
     Manager,
     Rkv,
 };
-
-use self::tempfile::Builder;
-
-use std::fs;
-
-use std::sync::Arc;
+use std::{
+    fs,
+    sync::Arc,
+};
+use tempfile::Builder;
 
 #[test]
 // Identical to the same-named unit test, but this one confirms that it works
 // via the public MANAGER singleton.
 fn test_same() {
     let root = Builder::new().prefix("test_same_singleton").tempdir().expect("tempdir");
     fs::create_dir_all(root.path()).expect("dir created");
 
new file mode 100644
--- /dev/null
+++ b/third_party/rust/rkv/tests/multi-integer-store.rs
@@ -0,0 +1,91 @@
+// Copyright 2018 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.
+
+use rkv::{
+    PrimitiveInt,
+    Rkv,
+    StoreOptions,
+    Value,
+};
+use serde_derive::Serialize;
+use std::fs;
+use tempfile::Builder;
+
+#[test]
+fn test_multi_integer_keys() {
+    let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
+    fs::create_dir_all(root.path()).expect("dir created");
+    let k = Rkv::new(root.path()).expect("new succeeded");
+    let s = k.open_multi_integer("s", StoreOptions::create()).expect("open");
+
+    macro_rules! test_integer_keys {
+        ($store:expr, $key:expr) => {{
+            let mut writer = k.write().expect("writer");
+
+            $store.put(&mut writer, $key, &Value::Str("hello1")).expect("write");
+            $store.put(&mut writer, $key, &Value::Str("hello2")).expect("write");
+            $store.put(&mut writer, $key, &Value::Str("hello3")).expect("write");
+            let vals = $store
+                .get(&writer, $key)
+                .expect("read")
+                .map(|result| result.expect("ok"))
+                .map(|(_, v)| v.expect("multi read"))
+                .collect::<Vec<Value>>();
+            assert_eq!(vals, vec![Value::Str("hello1"), Value::Str("hello2"), Value::Str("hello3")]);
+            writer.commit().expect("committed");
+
+            let reader = k.read().expect("reader");
+            let vals = $store
+                .get(&reader, $key)
+                .expect("read")
+                .map(|result| result.expect("ok"))
+                .map(|(_, v)| v.expect("multi read"))
+                .collect::<Vec<Value>>();
+            assert_eq!(vals, vec![Value::Str("hello1"), Value::Str("hello2"), Value::Str("hello3")]);
+        }};
+    }
+
+    // The integer module provides only the u32 integer key variant
+    // of IntegerStore, so we can use it without further ado.
+    test_integer_keys!(s, std::u32::MIN);
+    test_integer_keys!(s, std::u32::MAX);
+
+    // If you want to use another integer key variant, you need to implement
+    // a newtype, implement PrimitiveInt, and implement or derive Serialize
+    // for it.  Here we do so for the i32 type.
+
+    // DANGER!  Doing this enables you to open a store with multiple,
+    // different integer key types, which may result in unexpected behavior.
+    // Make sure you know what you're doing!
+
+    let t = k.open_multi_integer("s", StoreOptions::create()).expect("open");
+
+    #[derive(Serialize)]
+    struct I32(i32);
+    impl PrimitiveInt for I32 {}
+    test_integer_keys!(t, I32(std::i32::MIN));
+    test_integer_keys!(t, I32(std::i32::MAX));
+
+    let u = k.open_multi_integer("s", StoreOptions::create()).expect("open");
+
+    #[derive(Serialize)]
+    struct U16(u16);
+    impl PrimitiveInt for U16 {}
+    test_integer_keys!(u, U16(std::u16::MIN));
+    test_integer_keys!(u, U16(std::u16::MAX));
+
+    let v = k.open_multi_integer("s", StoreOptions::create()).expect("open");
+
+    #[derive(Serialize)]
+    struct U64(u64);
+    impl PrimitiveInt for U64 {}
+    test_integer_keys!(v, U64(std::u64::MIN));
+    test_integer_keys!(v, U64(std::u64::MAX));
+}
--- a/toolkit/components/kvstore/Cargo.toml
+++ b/toolkit/components/kvstore/Cargo.toml
@@ -2,23 +2,23 @@
 name = "kvstore"
 version = "0.1.0"
 authors = ["Myk Melez <myk@mykzilla.org>"]
 
 [dependencies]
 atomic_refcell = "0.1"
 crossbeam-utils = "0.6.3"
 libc = "0.2"
-lmdb-rkv = "0.9"
+lmdb-rkv = "0.11.2"
 log = "0.4"
 moz_task = { path = "../../../xpcom/rust/moz_task" }
 nserror = { path = "../../../xpcom/rust/nserror" }
 nsstring = { path = "../../../xpcom/rust/nsstring" }
 ordered-float = "1"
-rkv = "0.7"
+rkv = "0.9.2"
 storage_variant = { path = "../../../storage/variant" }
 xpcom = { path = "../../../xpcom/rust/xpcom" }
 
 # Get rid of failure's dependency on backtrace. Eventually
 # backtrace will move into Rust core, but we don't need it here.
 [dependencies.failure]
 version = "0.1"
 default_features = false
--- a/toolkit/components/kvstore/nsIKeyValue.idl
+++ b/toolkit/components/kvstore/nsIKeyValue.idl
@@ -29,34 +29,27 @@ interface nsIKeyValueVoidCallback;
 
 /**
  * The key/value service.  Enables retrieval of handles to key/value databases.
  */
 [scriptable, uuid(46c893dd-4c14-4de0-b33d-a1be18c6d062)]
 interface nsIKeyValueService : nsISupports {
     /**
      * Get a handle to an existing database or a newly-created one
-     * at the specified path and with the given name.  If no name is given,
-     * the default database is returned.
+     * at the specified path and with the given name.
      *
      * The service supports multiple named databases at the same path
      * (i.e. within the same storage file), so you can call this method
      * multiple times with the same path and different names to retrieve
      * multiple databases stored in the same location on disk.
-     *
-     * Because the backing storage engine stores information about named
-     * databases in the default database, it's tricky to use both the default
-     * and named databases at the same path.  Thus you should generally use
-     * either the default database or named databases, but not both, for a
-     * given path.
      */
     void getOrCreate(
         in nsIKeyValueDatabaseCallback callback,
         in AUTF8String path,
-        [optional] in AUTF8String name);
+        in AUTF8String name);
 };
 
 /**
  * A key/value database.
  *
  * All methods are asynchronous and take a callback as their first argument.
  * The types of the callbacks vary, but they can all be implemented in JS
  * via an object literal with the relevant methods.
--- a/toolkit/components/kvstore/src/lib.rs
+++ b/toolkit/components/kvstore/src/lib.rs
@@ -24,31 +24,33 @@ mod task;
 
 use atomic_refcell::AtomicRefCell;
 use error::KeyValueError;
 use libc::c_void;
 use moz_task::{create_thread, TaskRunnable};
 use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_NO_AGGREGATION, NS_OK};
 use nsstring::{nsACString, nsCString};
 use owned_value::{owned_to_variant, variant_to_owned, OwnedValue};
-use rkv::{Rkv, Store};
+use rkv::{Rkv, SingleStore};
 use std::{
     ptr,
     sync::{Arc, RwLock},
     vec::IntoIter,
 };
 use task::{DeleteTask, EnumerateTask, GetOrCreateTask, GetTask, HasTask, PutTask};
 use xpcom::{
     interfaces::{
         nsIKeyValueDatabaseCallback, nsIKeyValueEnumeratorCallback, nsIKeyValuePair,
         nsIKeyValueVariantCallback, nsIKeyValueVoidCallback, nsISupports, nsIThread, nsIVariant,
     },
     nsIID, RefPtr, ThreadBoundRefPtr,
 };
 
+type KeyValuePairResult = Result<(String, OwnedValue), KeyValueError>;
+
 #[no_mangle]
 pub unsafe extern "C" fn nsKeyValueServiceConstructor(
     outer: *const nsISupports,
     iid: &nsIID,
     result: *mut *mut c_void,
 ) -> nsresult {
     *result = ptr::null_mut();
 
@@ -129,24 +131,24 @@ impl KeyValueService {
     }
 }
 
 #[derive(xpcom)]
 #[xpimplements(nsIKeyValueDatabase)]
 #[refcnt = "atomic"]
 pub struct InitKeyValueDatabase {
     rkv: Arc<RwLock<Rkv>>,
-    store: Store,
+    store: SingleStore,
     thread: ThreadBoundRefPtr<nsIThread>,
 }
 
 impl KeyValueDatabase {
     fn new(
         rkv: Arc<RwLock<Rkv>>,
-        store: Store,
+        store: SingleStore,
         thread: ThreadBoundRefPtr<nsIThread>,
     ) -> RefPtr<KeyValueDatabase> {
         KeyValueDatabase::allocate(InitKeyValueDatabase { rkv, store, thread })
     }
 
     xpcom_method!(
         put => Put(
             callback: *const nsIKeyValueVoidCallback,
@@ -267,53 +269,45 @@ impl KeyValueDatabase {
         TaskRunnable::new("KVDatabase::Enumerate", task)?.dispatch(RefPtr::new(thread))
     }
 }
 
 #[derive(xpcom)]
 #[xpimplements(nsIKeyValueEnumerator)]
 #[refcnt = "atomic"]
 pub struct InitKeyValueEnumerator {
-    iter: AtomicRefCell<
-        IntoIter<(
-            Result<String, KeyValueError>,
-            Result<OwnedValue, KeyValueError>,
-        )>,
-    >,
+    iter: AtomicRefCell<IntoIter<KeyValuePairResult>>,
 }
 
 impl KeyValueEnumerator {
-    fn new(
-        pairs: Vec<(
-            Result<String, KeyValueError>,
-            Result<OwnedValue, KeyValueError>,
-        )>,
-    ) -> RefPtr<KeyValueEnumerator> {
+    fn new(pairs: Vec<KeyValuePairResult>) -> RefPtr<KeyValueEnumerator> {
         KeyValueEnumerator::allocate(InitKeyValueEnumerator {
             iter: AtomicRefCell::new(pairs.into_iter()),
         })
     }
 
     xpcom_method!(has_more_elements => HasMoreElements() -> bool);
 
     fn has_more_elements(&self) -> Result<bool, KeyValueError> {
         Ok(!self.iter.borrow().as_slice().is_empty())
     }
 
     xpcom_method!(get_next => GetNext() -> *const nsIKeyValuePair);
 
     fn get_next(&self) -> Result<RefPtr<nsIKeyValuePair>, KeyValueError> {
         let mut iter = self.iter.borrow_mut();
-        let (key, value) = iter.next().ok_or(KeyValueError::from(NS_ERROR_FAILURE))?;
+        let (key, value) = iter
+            .next()
+            .ok_or_else(|| KeyValueError::from(NS_ERROR_FAILURE))??;
 
         // We fail on retrieval of the key/value pair if the key isn't valid
         // UTF-*, if the value is unexpected, or if we encountered a store error
         // while retrieving the pair.
         Ok(RefPtr::new(
-            KeyValuePair::new(key?, value?).coerce::<nsIKeyValuePair>(),
+            KeyValuePair::new(key, value).coerce::<nsIKeyValuePair>(),
         ))
     }
 }
 
 #[derive(xpcom)]
 #[xpimplements(nsIKeyValuePair)]
 #[refcnt = "atomic"]
 pub struct InitKeyValuePair {
--- a/toolkit/components/kvstore/src/owned_value.rs
+++ b/toolkit/components/kvstore/src/owned_value.rs
@@ -19,17 +19,17 @@ use xpcom::{interfaces::nsIVariant, RefP
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum OwnedValue {
     Bool(bool),
     I64(i64),
     F64(OrderedFloat<f64>),
     Str(String),
 }
 
-pub fn value_to_owned<'a>(value: Option<Value<'a>>) -> Result<OwnedValue, KeyValueError> {
+pub fn value_to_owned(value: Option<Value>) -> Result<OwnedValue, KeyValueError> {
     match value {
         Some(Value::Bool(val)) => Ok(OwnedValue::Bool(val)),
         Some(Value::I64(val)) => Ok(OwnedValue::I64(val)),
         Some(Value::F64(val)) => Ok(OwnedValue::F64(val)),
         Some(Value::Str(val)) => Ok(OwnedValue::Str(val.to_owned())),
         Some(_value) => Err(KeyValueError::UnexpectedValue),
         None => Err(KeyValueError::UnexpectedValue),
     }
@@ -65,13 +65,11 @@ pub fn variant_to_owned(variant: &nsIVar
             Ok(Some(OwnedValue::Str(str)))
         }
         DATA_TYPE_BOOL => {
             let mut val: bool = false;
             unsafe { variant.GetAsBool(&mut val) }.to_result()?;
             Ok(Some(OwnedValue::Bool(val)))
         }
         DATA_TYPE_EMPTY | DATA_TYPE_VOID => Ok(None),
-        unsupported_type => {
-            return Err(KeyValueError::UnsupportedType(unsupported_type));
-        }
+        unsupported_type => Err(KeyValueError::UnsupportedType(unsupported_type)),
     }
 }
--- a/toolkit/components/kvstore/src/task.rs
+++ b/toolkit/components/kvstore/src/task.rs
@@ -5,33 +5,33 @@
 extern crate xpcom;
 
 use crossbeam_utils::atomic::AtomicCell;
 use error::KeyValueError;
 use moz_task::Task;
 use nserror::{nsresult, NsresultExt, NS_ERROR_FAILURE};
 use nsstring::{nsCString, nsString};
 use owned_value::{value_to_owned, OwnedValue};
-use rkv::{Manager, Rkv, Store, StoreError, Value};
+use rkv::{Manager, Rkv, SingleStore, StoreError, StoreOptions, Value};
 use std::{
     path::Path,
     str,
     sync::{Arc, RwLock},
 };
 use storage_variant::VariantType;
 use xpcom::{
     interfaces::{
         nsIKeyValueDatabaseCallback, nsIKeyValueEnumeratorCallback, nsIKeyValueVariantCallback,
         nsIKeyValueVoidCallback, nsIThread, nsIVariant,
     },
-    RefPtr,
-    ThreadBoundRefPtr,
+    RefPtr, ThreadBoundRefPtr,
 };
 use KeyValueDatabase;
 use KeyValueEnumerator;
+use KeyValuePairResult;
 
 /// A macro to generate a done() implementation for a Task.
 /// Takes one argument that specifies the type of the Task's callback function:
 ///   value: a callback function that takes a value
 ///   void: the callback function doesn't take a value
 ///
 /// The "value" variant calls self.convert() to convert a successful result
 /// into the value to pass to the callback function.  So if you generate done()
@@ -73,22 +73,29 @@ macro_rules! task_done {
                 Some(Ok(())) => unsafe { callback.Resolve() },
                 Some(Err(err)) => unsafe { callback.Reject(&*nsCString::from(err.to_string())) },
                 None => unsafe { callback.Reject(&*nsCString::from("unexpected")) },
             }.to_result()
         }
     };
 }
 
+/// A tuple comprising an Arc<RwLock<Rkv>> and a SingleStore, which is
+/// the result of GetOrCreateTask.  We declare this type because otherwise
+/// Clippy complains "error: very complex type used. Consider factoring
+/// parts into `type` definitions" (i.e. clippy::type-complexity) when we
+/// declare the type of `GetOrCreateTask::result`.
+type RkvStoreTuple = (Arc<RwLock<Rkv>>, SingleStore);
+
 pub struct GetOrCreateTask {
     callback: AtomicCell<Option<ThreadBoundRefPtr<nsIKeyValueDatabaseCallback>>>,
     thread: AtomicCell<Option<ThreadBoundRefPtr<nsIThread>>>,
     path: nsCString,
     name: nsCString,
-    result: AtomicCell<Option<Result<(Arc<RwLock<Rkv>>, Store), KeyValueError>>>,
+    result: AtomicCell<Option<Result<RkvStoreTuple, KeyValueError>>>,
 }
 
 impl GetOrCreateTask {
     pub fn new(
         callback: RefPtr<nsIKeyValueDatabaseCallback>,
         thread: RefPtr<nsIThread>,
         path: nsCString,
         name: nsCString,
@@ -97,61 +104,54 @@ impl GetOrCreateTask {
             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
             thread: AtomicCell::new(Some(ThreadBoundRefPtr::new(thread))),
             path,
             name,
             result: AtomicCell::default(),
         }
     }
 
-    fn convert(
-        &self,
-        result: (Arc<RwLock<Rkv>>, Store),
-    ) -> Result<RefPtr<KeyValueDatabase>, KeyValueError> {
+    fn convert(&self, result: RkvStoreTuple) -> Result<RefPtr<KeyValueDatabase>, KeyValueError> {
         let thread = self.thread.swap(None).ok_or(NS_ERROR_FAILURE)?;
         Ok(KeyValueDatabase::new(result.0, result.1, thread))
     }
 }
 
 impl Task for GetOrCreateTask {
     fn run(&self) {
         // We do the work within a closure that returns a Result so we can
         // use the ? operator to simplify the implementation.
-        self.result.store(Some(
-            || -> Result<(Arc<RwLock<Rkv>>, Store), KeyValueError> {
+        self.result
+            .store(Some(|| -> Result<RkvStoreTuple, KeyValueError> {
                 let mut writer = Manager::singleton().write()?;
                 let rkv = writer.get_or_create(Path::new(str::from_utf8(&self.path)?), Rkv::new)?;
-                let store = if self.name.is_empty() {
-                    rkv.write()?.open_or_create_default()
-                } else {
-                    rkv.write()?
-                        .open_or_create(Some(str::from_utf8(&self.name)?))
-                }?;
+                let store = rkv
+                    .write()?
+                    .open_single(str::from_utf8(&self.name)?, StoreOptions::create())?;
                 Ok((rkv, store))
-            }(),
-        ));
+            }()));
     }
 
     task_done!(value);
 }
 
 pub struct PutTask {
     callback: AtomicCell<Option<ThreadBoundRefPtr<nsIKeyValueVoidCallback>>>,
     rkv: Arc<RwLock<Rkv>>,
-    store: Store,
+    store: SingleStore,
     key: nsCString,
     value: OwnedValue,
     result: AtomicCell<Option<Result<(), KeyValueError>>>,
 }
 
 impl PutTask {
     pub fn new(
         callback: RefPtr<nsIKeyValueVoidCallback>,
         rkv: Arc<RwLock<Rkv>>,
-        store: Store,
+        store: SingleStore,
         key: nsCString,
         value: OwnedValue,
     ) -> PutTask {
         PutTask {
             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
             rkv,
             store,
             key,
@@ -172,40 +172,40 @@ impl Task for PutTask {
 
             let value = match self.value {
                 OwnedValue::Bool(val) => Value::Bool(val),
                 OwnedValue::I64(val) => Value::I64(val),
                 OwnedValue::F64(val) => Value::F64(val),
                 OwnedValue::Str(ref val) => Value::Str(&val),
             };
 
-            writer.put(self.store, key, &value)?;
+            self.store.put(&mut writer, key, &value)?;
             writer.commit()?;
 
             Ok(())
         }()));
     }
 
     task_done!(void);
 }
 
 pub struct GetTask {
     callback: AtomicCell<Option<ThreadBoundRefPtr<nsIKeyValueVariantCallback>>>,
     rkv: Arc<RwLock<Rkv>>,
-    store: Store,
+    store: SingleStore,
     key: nsCString,
     default_value: Option<OwnedValue>,
     result: AtomicCell<Option<Result<Option<OwnedValue>, KeyValueError>>>,
 }
 
 impl GetTask {
     pub fn new(
         callback: RefPtr<nsIKeyValueVariantCallback>,
         rkv: Arc<RwLock<Rkv>>,
-        store: Store,
+        store: SingleStore,
         key: nsCString,
         default_value: Option<OwnedValue>,
     ) -> GetTask {
         GetTask {
             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
             rkv,
             store,
             key,
@@ -230,17 +230,17 @@ impl Task for GetTask {
     fn run(&self) {
         // We do the work within a closure that returns a Result so we can
         // use the ? operator to simplify the implementation.
         self.result
             .store(Some(|| -> Result<Option<OwnedValue>, KeyValueError> {
                 let key = str::from_utf8(&self.key)?;
                 let env = self.rkv.read()?;
                 let reader = env.read()?;
-                let value = reader.get(self.store, key)?;
+                let value = self.store.get(&reader, key)?;
 
                 // TODO: refactor with value_to_owned in owned_value.rs.
                 Ok(match value {
                     Some(Value::Bool(val)) => Some(OwnedValue::Bool(val)),
                     Some(Value::I64(val)) => Some(OwnedValue::I64(val)),
                     Some(Value::F64(val)) => Some(OwnedValue::F64(val)),
                     Some(Value::Str(val)) => Some(OwnedValue::Str(val.to_owned())),
                     Some(_value) => return Err(KeyValueError::UnexpectedValue),
@@ -253,26 +253,26 @@ impl Task for GetTask {
     }
 
     task_done!(value);
 }
 
 pub struct HasTask {
     callback: AtomicCell<Option<ThreadBoundRefPtr<nsIKeyValueVariantCallback>>>,
     rkv: Arc<RwLock<Rkv>>,
-    store: Store,
+    store: SingleStore,
     key: nsCString,
     result: AtomicCell<Option<Result<bool, KeyValueError>>>,
 }
 
 impl HasTask {
     pub fn new(
         callback: RefPtr<nsIKeyValueVariantCallback>,
         rkv: Arc<RwLock<Rkv>>,
-        store: Store,
+        store: SingleStore,
         key: nsCString,
     ) -> HasTask {
         HasTask {
             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
             rkv,
             store,
             key,
             result: AtomicCell::default(),
@@ -287,37 +287,37 @@ impl HasTask {
 impl Task for HasTask {
     fn run(&self) {
         // We do the work within a closure that returns a Result so we can
         // use the ? operator to simplify the implementation.
         self.result.store(Some(|| -> Result<bool, KeyValueError> {
             let key = str::from_utf8(&self.key)?;
             let env = self.rkv.read()?;
             let reader = env.read()?;
-            let value = reader.get(self.store, key)?;
+            let value = self.store.get(&reader, key)?;
             Ok(value.is_some())
         }()));
     }
 
     task_done!(value);
 }
 
 pub struct DeleteTask {
     callback: AtomicCell<Option<ThreadBoundRefPtr<nsIKeyValueVoidCallback>>>,
     rkv: Arc<RwLock<Rkv>>,
-    store: Store,
+    store: SingleStore,
     key: nsCString,
     result: AtomicCell<Option<Result<(), KeyValueError>>>,
 }
 
 impl DeleteTask {
     pub fn new(
         callback: RefPtr<nsIKeyValueVoidCallback>,
         rkv: Arc<RwLock<Rkv>>,
-        store: Store,
+        store: SingleStore,
         key: nsCString,
     ) -> DeleteTask {
         DeleteTask {
             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
             rkv,
             store,
             key,
             result: AtomicCell::default(),
@@ -329,17 +329,17 @@ impl Task for DeleteTask {
     fn run(&self) {
         // We do the work within a closure that returns a Result so we can
         // use the ? operator to simplify the implementation.
         self.result.store(Some(|| -> Result<(), KeyValueError> {
             let key = str::from_utf8(&self.key)?;
             let env = self.rkv.read()?;
             let mut writer = env.write()?;
 
-            match writer.delete(self.store, key) {
+            match self.store.delete(&mut writer, key) {
                 Ok(_) => (),
 
                 // LMDB fails with an error if the key to delete wasn't found,
                 // and Rkv returns that error, but we ignore it, as we expect most
                 // of our consumers to want this behavior.
                 Err(StoreError::LmdbError(lmdb::Error::NotFound)) => (),
 
                 Err(err) => return Err(KeyValueError::StoreError(err)),
@@ -352,108 +352,108 @@ impl Task for DeleteTask {
     }
 
     task_done!(void);
 }
 
 pub struct EnumerateTask {
     callback: AtomicCell<Option<ThreadBoundRefPtr<nsIKeyValueEnumeratorCallback>>>,
     rkv: Arc<RwLock<Rkv>>,
-    store: Store,
+    store: SingleStore,
     from_key: nsCString,
     to_key: nsCString,
-    result: AtomicCell<Option<Result<Vec<KeyValuePair>, KeyValueError>>>,
+    result: AtomicCell<Option<Result<Vec<KeyValuePairResult>, KeyValueError>>>,
 }
 
 impl EnumerateTask {
     pub fn new(
         callback: RefPtr<nsIKeyValueEnumeratorCallback>,
         rkv: Arc<RwLock<Rkv>>,
-        store: Store,
+        store: SingleStore,
         from_key: nsCString,
         to_key: nsCString,
     ) -> EnumerateTask {
         EnumerateTask {
             callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
             rkv,
             store,
             from_key,
             to_key,
             result: AtomicCell::default(),
         }
     }
 
     fn convert(
         &self,
-        result: Vec<KeyValuePair>,
+        result: Vec<KeyValuePairResult>,
     ) -> Result<RefPtr<KeyValueEnumerator>, KeyValueError> {
         Ok(KeyValueEnumerator::new(result))
     }
 }
 
-type KeyValuePair = (
-    Result<String, KeyValueError>,
-    Result<OwnedValue, KeyValueError>,
-);
-
 impl Task for EnumerateTask {
     fn run(&self) {
         // We do the work within a closure that returns a Result so we can
         // use the ? operator to simplify the implementation.
-        self.result
-            .store(Some(|| -> Result<Vec<KeyValuePair>, KeyValueError> {
+        self.result.store(Some(
+            || -> Result<Vec<KeyValuePairResult>, KeyValueError> {
                 let env = self.rkv.read()?;
                 let reader = env.read()?;
                 let from_key = str::from_utf8(&self.from_key)?;
                 let to_key = str::from_utf8(&self.to_key)?;
 
                 let iterator = if from_key.is_empty() {
-                    reader.iter_start(self.store)?
+                    self.store.iter_start(&reader)?
                 } else {
-                    reader.iter_from(self.store, &from_key)?
+                    self.store.iter_from(&reader, &from_key)?
                 };
 
                 // Ideally, we'd enumerate pairs lazily, as the consumer calls
                 // nsIKeyValueEnumerator.getNext(), which calls our
                 // KeyValueEnumerator.get_next() implementation.  But KeyValueEnumerator
                 // can't reference the Iter because Rust "cannot #[derive(xpcom)]
                 // on a generic type," and the Iter requires a lifetime parameter,
                 // which would make KeyValueEnumerator generic.
                 //
                 // Our fallback approach is to eagerly collect the iterator
                 // into a collection that KeyValueEnumerator owns.  Fixing this so we
                 // enumerate pairs lazily is bug 1499252.
-                let pairs: Vec<KeyValuePair> = iterator
+                let pairs: Vec<KeyValuePairResult> = iterator
                     // Convert the key to a string so we can compare it to the "to" key.
                     // For forward compatibility, we don't fail here if we can't convert
                     // a key to UTF-8.  Instead, we store the Err in the collection
                     // and fail lazily in KeyValueEnumerator.get_next().
-                    .map(|(key, val)| (str::from_utf8(&key), val))
-                    .take_while(|(key, _val)| {
-                        if to_key.is_empty() {
-                            true
-                        } else {
-                            match *key {
-                                Ok(key) => key < to_key,
-                                Err(_err) => true,
+                    .map(|result| match result {
+                        Ok((key, val)) => Ok((str::from_utf8(&key), val)),
+                        Err(err) => Err(err),
+                    })
+                    // Stop iterating once we reach the to_key, if any.
+                    .take_while(|result| match result {
+                        Ok((key, _val)) => {
+                            if to_key.is_empty() {
+                                true
+                            } else {
+                                match *key {
+                                    Ok(key) => key < to_key,
+                                    Err(_err) => true,
+                                }
                             }
                         }
+                        Err(_) => true,
                     })
-                    .map(|(key, val)| {
-                        (
-                            match key {
-                                Ok(key) => Ok(key.to_owned()),
-                                Err(err) => Err(err.into()),
-                            },
-                            match val {
-                                Ok(val) => value_to_owned(val),
-                                Err(err) => Err(KeyValueError::StoreError(err)),
-                            },
-                        )
+                    // Convert the key/value pair to owned.
+                    .map(|result| match result {
+                        Ok((key, val)) => match (key, value_to_owned(val)) {
+                            (Ok(key), Ok(val)) => Ok((key.to_owned(), val)),
+                            (Err(err), _) => Err(err.into()),
+                            (_, Err(err)) => Err(err),
+                        },
+                        Err(err) => Err(KeyValueError::StoreError(err)),
                     })
                     .collect();
 
                 Ok(pairs)
-            }()));
+            }(),
+        ));
     }
 
     task_done!(value);
 }
--- a/toolkit/components/kvstore/test/xpcshell/test_kvstore.js
+++ b/toolkit/components/kvstore/test/xpcshell/test_kvstore.js
@@ -21,23 +21,23 @@ const gKeyValueService =
   Cc["@mozilla.org/key-value-service;1"].getService(Ci.nsIKeyValueService);
 
 add_task(async function getService() {
   Assert.ok(gKeyValueService);
 });
 
 add_task(async function getOrCreate() {
   const databaseDir = await makeDatabaseDir("getOrCreate");
-  const defaultDatabase = await KeyValueService.getOrCreate(databaseDir);
-  Assert.ok(defaultDatabase);
+  const database = await KeyValueService.getOrCreate(databaseDir, "db");
+  Assert.ok(database);
 });
 
 add_task(async function putGetHasDelete() {
   const databaseDir = await makeDatabaseDir("putGetHasDelete");
-  const database = await KeyValueService.getOrCreate(databaseDir);
+  const database = await KeyValueService.getOrCreate(databaseDir, "db");
 
   // Getting key/value pairs that don't exist (yet) returns default values
   // or null, depending on whether you specify a default value.
   Assert.strictEqual(await database.get("int-key", 1), 1);
   Assert.strictEqual(await database.get("double-key", 1.1), 1.1);
   Assert.strictEqual(await database.get("string-key", ""), "");
   Assert.strictEqual(await database.get("bool-key", false), false);
   Assert.strictEqual(await database.get("int-key"), null);
@@ -88,17 +88,17 @@ add_task(async function putGetHasDelete(
   Assert.strictEqual(await database.get("int-key"), null);
   Assert.strictEqual(await database.get("double-key"), null);
   Assert.strictEqual(await database.get("string-key"), null);
   Assert.strictEqual(await database.get("bool-key"), null);
 });
 
 add_task(async function largeNumbers() {
   const databaseDir = await makeDatabaseDir("largeNumbers");
-  const database = await KeyValueService.getOrCreate(databaseDir);
+  const database = await KeyValueService.getOrCreate(databaseDir, "db");
 
   const MAX_INT_VARIANT = Math.pow(2, 31) - 1;
   const MIN_DOUBLE_VARIANT = Math.pow(2, 31);
 
   await database.put("max-int-variant", MAX_INT_VARIANT);
   await database.put("min-double-variant", MIN_DOUBLE_VARIANT);
   await database.put("max-safe-integer", Number.MAX_SAFE_INTEGER);
   await database.put("min-safe-integer", Number.MIN_SAFE_INTEGER);
@@ -110,17 +110,17 @@ add_task(async function largeNumbers() {
   Assert.strictEqual(await database.get("max-safe-integer"), Number.MAX_SAFE_INTEGER);
   Assert.strictEqual(await database.get("min-safe-integer"), Number.MIN_SAFE_INTEGER);
   Assert.strictEqual(await database.get("max-value"), Number.MAX_VALUE);
   Assert.strictEqual(await database.get("min-value"), Number.MIN_VALUE);
 });
 
 add_task(async function extendedCharacterKey() {
   const databaseDir = await makeDatabaseDir("extendedCharacterKey");
-  const database = await KeyValueService.getOrCreate(databaseDir);
+  const database = await KeyValueService.getOrCreate(databaseDir, "db");
 
   // Ensure that we can use extended character (i.e. non-ASCII) strings as keys.
 
   await database.put("Héllo, wőrld!", 1);
   Assert.strictEqual(await database.has("Héllo, wőrld!"), true);
   Assert.strictEqual(await database.get("Héllo, wőrld!"), 1);
 
   const enumerator = await database.enumerate();
@@ -134,52 +134,40 @@ add_task(async function getOrCreateNamed
   const databaseDir = await makeDatabaseDir("getOrCreateNamedDatabases");
 
   let fooDB = await KeyValueService.getOrCreate(databaseDir, "foo");
   Assert.ok(fooDB, "retrieval of first named database works");
 
   let barDB = await KeyValueService.getOrCreate(databaseDir, "bar");
   Assert.ok(barDB, "retrieval of second named database works");
 
-  let defaultDB = await KeyValueService.getOrCreate(databaseDir);
-  Assert.ok(defaultDB, "retrieval of default database works");
+  let bazDB = await KeyValueService.getOrCreate(databaseDir, "baz");
+  Assert.ok(bazDB, "retrieval of third named database works");
 
   // Key/value pairs that are put into a database don't exist in others.
-  await defaultDB.put("key", 1);
+  await bazDB.put("key", 1);
   Assert.ok(!(await fooDB.has("key")), "the foo DB still doesn't have the key");
   await fooDB.put("key", 2);
   Assert.ok(!(await barDB.has("key")), "the bar DB still doesn't have the key");
   await barDB.put("key", 3);
-  Assert.strictEqual(await defaultDB.get("key", 0), 1, "the default DB has its KV pair");
+  Assert.strictEqual(await bazDB.get("key", 0), 1, "the baz DB has its KV pair");
   Assert.strictEqual(await fooDB.get("key", 0), 2, "the foo DB has its KV pair");
   Assert.strictEqual(await barDB.get("key", 0), 3, "the bar DB has its KV pair");
 
   // Key/value pairs that are deleted from a database still exist in other DBs.
-  await defaultDB.delete("key");
+  await bazDB.delete("key");
   Assert.strictEqual(await fooDB.get("key", 0), 2, "the foo DB still has its KV pair");
   await fooDB.delete("key");
   Assert.strictEqual(await barDB.get("key", 0), 3, "the bar DB still has its KV pair");
   await barDB.delete("key");
-
-  // LMDB uses the default database to store information about named databases,
-  // so it's tricky to use both in the same directory (i.e. LMDB environment).
-
-  // If you try to put a key into the default database with the same name as
-  // a named database, then the write will fail because LMDB doesn't let you
-  // overwrite the key.
-  await Assert.rejects(defaultDB.put("foo", 5), /LmdbError\(Incompatible\)/);
-
-  // If you try to get a key from the default database for a named database,
-  // then the read will fail because rkv doesn't understand the key's data type.
-  await Assert.rejects(defaultDB.get("foo"), /DataError\(UnknownType\(0\)\)/);
 });
 
 add_task(async function enumeration() {
   const databaseDir = await makeDatabaseDir("enumeration");
-  const database = await KeyValueService.getOrCreate(databaseDir);
+  const database = await KeyValueService.getOrCreate(databaseDir, "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);
 
   async function test(fromKey, toKey, pairs) {
     const enumerator = await database.enumerate(fromKey, toKey);
--- a/xpcom/rust/moz_task/src/lib.rs
+++ b/xpcom/rust/moz_task/src/lib.rs
@@ -72,41 +72,46 @@ pub trait Task {
 #[refcnt = "atomic"]
 pub struct InitTaskRunnable {
     name: &'static str,
     task: Box<dyn Task + Send + Sync>,
     has_run: AtomicBool,
 }
 
 impl TaskRunnable {
-    pub fn new(name: &'static str, task: Box<dyn Task + Send + Sync>) -> Result<RefPtr<TaskRunnable>, nsresult> {
+    pub fn new(
+        name: &'static str,
+        task: Box<dyn Task + Send + Sync>,
+    ) -> Result<RefPtr<TaskRunnable>, nsresult> {
         assert!(is_main_thread());
         Ok(TaskRunnable::allocate(InitTaskRunnable {
             name,
             task,
             has_run: AtomicBool::new(false),
         }))
     }
     pub fn dispatch(&self, target_thread: RefPtr<nsIThread>) -> Result<(), nsresult> {
         unsafe {
             target_thread.DispatchFromScript(self.coerce(), nsIEventTarget::DISPATCH_NORMAL as u32)
         }
         .to_result()
     }
 
     xpcom_method!(run => Run());
     fn run(&self) -> Result<(), nsresult> {
-        match self.has_run.load(Ordering::Acquire) {
-            false => {
+        match self
+            .has_run
+            .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
+        {
+            Ok(_) => {
                 assert!(!is_main_thread());
-                self.has_run.store(true, Ordering::Release);
                 self.task.run();
                 self.dispatch(get_main_thread()?)
             }
-            true => {
+            Err(_) => {
                 assert!(is_main_thread());
                 self.task.done()
             }
         }
     }
 
     xpcom_method!(get_name => GetName() -> nsACString);
     fn get_name(&self) -> Result<nsCString, nsresult> {