database: Resizes, Shutdown, Flushing (#68)
* error: add `NeedsResize`
accidently removed, was `MapFull` before.
We need this because we as the writer thread must
react to this error in order to resize.
The writer thread doesn't have access to `heed::Error`, but
`Runtime::Error`, so this variant must exist
* env/backend: add `MANUAL_RESIZE` and `resize()`
* heed: respect `ReadersFull`, comment errors
* free: add `resize_memory_map()`
* env: add `Env::current_map_size`
* service: add `resize_map()`
* database: make `Env` itself cheaply clonable + threadsafe
`heed::Env` already uses `Arc` internally, but `sanakirja` does
not, so abstract this at the `Env` level instead of wrapping
`ConcreteEnv` in `Arc` ourselves.
* service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone`
* env: add `SYNCS_PER_TX`, `path()`, `shutdown()`
* database: add `src/service/state.rs`
* service: add to `state.rs`, add `DatabaseState` to readers/writer
* add `parking_lot`
Okay, turns out we need to take locks in
`database/src/service/state.rs`...
`std`'s lock fairness policies are not well defined and
depend on the OS implementation, `parking_lot` on the other hand
has a fairness policy which is important when the writer needs
the lock but readers keep pouring in, essentially never letting
the writer do stuff.
* state: use `crossbeam::atomic::AtomicCell`
We have crossbeam as a dep anyway.
* heed: `heed::Env` -> `Arc<RwLock<heed::Env>>`
* service: add reader shutdown handle, use `Select` for msgs
* service: remove `state.rs`
We don't need this, we will represent shutdowns with channel
messages and `Select`, and mutual exclusion with a `RwLock`.
* service: fix misc reader/writer stuff
* database: add `config.rs`
* service: use `ReaderThreads` when spawning readers
* service: impl `shutdown()` for readers/writer
* service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle`
Solves the issue of unfortunately timed `Request`s
that come in _right_ as we are shutting down.
If we (Cuprate) drop the database channels too early the requesting
thread will probably panic as they probably use `.unwrap()`,
never expecting a channel failure.
Returning this structure containing all the channels allows the
final shutdown code to carry these channels until the very end
of the program, at which point, all threads exit - no panics.
* remove `parking_lot`
Could be used as the database mutual exclusion lock.
Needs to be tested against `std`.
* config: add `path`
* env: `path()` -> `config()`, backend: impl `Drop`
* `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect
* backend: add `Config` to `ConcreteEnv`
* service: use `std::thread::Builder` for readers/writer
* config: `PathBuf` -> `Cow<'static, Path>`
* misc docs, remove `RuntimeError::ShuttingDown`
* service: init & shutdown docs
* heed: impl `Env::resize_map()`
* heed: impl `Env::current_map_size()`
* lib.rs: add example crate usage test
* heed: `RwLock` comment
* helper: add `cuprate_database_dir()`
* config: use `cuprate_database_dir()`
* lib.rs: TODO example test
* database: add `page_size`
The database memory map size must be a multiple of
the OS page size. Why doesn't `heed` re-expose this?
It calls it when checking anyway...
https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772
* free: impl `resize_memory_map()`
* free: docs
* env: add `disk_size_bytes()`
* config: impl `From<$num>` for `ReaderThreads`
* move `fs`-related constants to `cuprate_helper::fs`
* docs
* add `resize.rs`, `ResizeAlgorithm`
* env: use `ResizeAlgorithm` in `resize_map()`
* TODO: account for LMDB reader limit
* resize: docs, add `page_size()`, impl `fixed_bytes()`
* resize: impl `percent()`
* resize: docs
* env: take `ResizeAlgorithm` by value (it impls `Copy`)
* heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED`
* config: `From<Into<usize>>` for `ReaderThreads`
Co-authored-by: Boog900 <boog900@tutanota.com>
* env: move mutual exclusion doc to backend
* free: update invariant doc
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/service/mod.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* fix `[allow(unused_imports)] // docs`
* move DB filename from `helper/` -> `database/`
* config: use `DATABASE_FILENAME`
* config: add `db_file_path()`
* lib: add non-`service` usage invariant docs
* table: seal `Table` trait, impl for all `crate::tables`
* fix docs
* fix docs pt.2
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-02-25 19:46:36 +00:00
|
|
|
|
//! Database memory map resizing algorithms.
|
|
|
|
|
//!
|
|
|
|
|
//! This modules contains [`ResizeAlgorithm`] which determines how the
|
2024-05-05 14:21:28 +00:00
|
|
|
|
//! [`ConcreteEnv`](crate::ConcreteEnv) resizes its memory map when needing more space.
|
database: Resizes, Shutdown, Flushing (#68)
* error: add `NeedsResize`
accidently removed, was `MapFull` before.
We need this because we as the writer thread must
react to this error in order to resize.
The writer thread doesn't have access to `heed::Error`, but
`Runtime::Error`, so this variant must exist
* env/backend: add `MANUAL_RESIZE` and `resize()`
* heed: respect `ReadersFull`, comment errors
* free: add `resize_memory_map()`
* env: add `Env::current_map_size`
* service: add `resize_map()`
* database: make `Env` itself cheaply clonable + threadsafe
`heed::Env` already uses `Arc` internally, but `sanakirja` does
not, so abstract this at the `Env` level instead of wrapping
`ConcreteEnv` in `Arc` ourselves.
* service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone`
* env: add `SYNCS_PER_TX`, `path()`, `shutdown()`
* database: add `src/service/state.rs`
* service: add to `state.rs`, add `DatabaseState` to readers/writer
* add `parking_lot`
Okay, turns out we need to take locks in
`database/src/service/state.rs`...
`std`'s lock fairness policies are not well defined and
depend on the OS implementation, `parking_lot` on the other hand
has a fairness policy which is important when the writer needs
the lock but readers keep pouring in, essentially never letting
the writer do stuff.
* state: use `crossbeam::atomic::AtomicCell`
We have crossbeam as a dep anyway.
* heed: `heed::Env` -> `Arc<RwLock<heed::Env>>`
* service: add reader shutdown handle, use `Select` for msgs
* service: remove `state.rs`
We don't need this, we will represent shutdowns with channel
messages and `Select`, and mutual exclusion with a `RwLock`.
* service: fix misc reader/writer stuff
* database: add `config.rs`
* service: use `ReaderThreads` when spawning readers
* service: impl `shutdown()` for readers/writer
* service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle`
Solves the issue of unfortunately timed `Request`s
that come in _right_ as we are shutting down.
If we (Cuprate) drop the database channels too early the requesting
thread will probably panic as they probably use `.unwrap()`,
never expecting a channel failure.
Returning this structure containing all the channels allows the
final shutdown code to carry these channels until the very end
of the program, at which point, all threads exit - no panics.
* remove `parking_lot`
Could be used as the database mutual exclusion lock.
Needs to be tested against `std`.
* config: add `path`
* env: `path()` -> `config()`, backend: impl `Drop`
* `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect
* backend: add `Config` to `ConcreteEnv`
* service: use `std::thread::Builder` for readers/writer
* config: `PathBuf` -> `Cow<'static, Path>`
* misc docs, remove `RuntimeError::ShuttingDown`
* service: init & shutdown docs
* heed: impl `Env::resize_map()`
* heed: impl `Env::current_map_size()`
* lib.rs: add example crate usage test
* heed: `RwLock` comment
* helper: add `cuprate_database_dir()`
* config: use `cuprate_database_dir()`
* lib.rs: TODO example test
* database: add `page_size`
The database memory map size must be a multiple of
the OS page size. Why doesn't `heed` re-expose this?
It calls it when checking anyway...
https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772
* free: impl `resize_memory_map()`
* free: docs
* env: add `disk_size_bytes()`
* config: impl `From<$num>` for `ReaderThreads`
* move `fs`-related constants to `cuprate_helper::fs`
* docs
* add `resize.rs`, `ResizeAlgorithm`
* env: use `ResizeAlgorithm` in `resize_map()`
* TODO: account for LMDB reader limit
* resize: docs, add `page_size()`, impl `fixed_bytes()`
* resize: impl `percent()`
* resize: docs
* env: take `ResizeAlgorithm` by value (it impls `Copy`)
* heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED`
* config: `From<Into<usize>>` for `ReaderThreads`
Co-authored-by: Boog900 <boog900@tutanota.com>
* env: move mutual exclusion doc to backend
* free: update invariant doc
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/service/mod.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* fix `[allow(unused_imports)] // docs`
* move DB filename from `helper/` -> `database/`
* config: use `DATABASE_FILENAME`
* config: add `db_file_path()`
* lib: add non-`service` usage invariant docs
* table: seal `Table` trait, impl for all `crate::tables`
* fix docs
* fix docs pt.2
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-02-25 19:46:36 +00:00
|
|
|
|
//! This value is in [`Config`](crate::config::Config) and can be selected at runtime.
|
|
|
|
|
//!
|
|
|
|
|
//! Although, it is only used by `ConcreteEnv` if [`Env::MANUAL_RESIZE`](crate::env::Env::MANUAL_RESIZE) is `true`.
|
|
|
|
|
//!
|
|
|
|
|
//! The algorithms are available as free functions in this module as well.
|
|
|
|
|
//!
|
|
|
|
|
//! # Page size
|
|
|
|
|
//! All free functions in this module will
|
|
|
|
|
//! return a multiple of the OS page size ([`page_size()`]),
|
|
|
|
|
//! [LMDB will error](http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5)
|
|
|
|
|
//! if this is not the case.
|
|
|
|
|
//!
|
|
|
|
|
//! # Invariants
|
|
|
|
|
//! All returned [`NonZeroUsize`] values of the free functions in this module
|
|
|
|
|
//! (including [`ResizeAlgorithm::resize`]) uphold the following invariants:
|
|
|
|
|
//! 1. It will always be `>=` the input `current_size_bytes`
|
|
|
|
|
//! 2. It will always be a multiple of [`page_size()`]
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- Import
|
|
|
|
|
use std::{num::NonZeroUsize, sync::OnceLock};
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- ResizeAlgorithm
|
|
|
|
|
/// The function/algorithm used by the
|
|
|
|
|
/// database when resizing the memory map.
|
|
|
|
|
///
|
2024-05-05 14:21:28 +00:00
|
|
|
|
// # SOMEDAY
|
|
|
|
|
// We could test around with different algorithms.
|
|
|
|
|
// Calling `heed::Env::resize` is surprisingly fast,
|
|
|
|
|
// around `0.0000082s` on my machine. We could probably
|
|
|
|
|
// get away with smaller and more frequent resizes.
|
|
|
|
|
// **With the caveat being we are taking a `WriteGuard` to a `RwLock`.**
|
database: Resizes, Shutdown, Flushing (#68)
* error: add `NeedsResize`
accidently removed, was `MapFull` before.
We need this because we as the writer thread must
react to this error in order to resize.
The writer thread doesn't have access to `heed::Error`, but
`Runtime::Error`, so this variant must exist
* env/backend: add `MANUAL_RESIZE` and `resize()`
* heed: respect `ReadersFull`, comment errors
* free: add `resize_memory_map()`
* env: add `Env::current_map_size`
* service: add `resize_map()`
* database: make `Env` itself cheaply clonable + threadsafe
`heed::Env` already uses `Arc` internally, but `sanakirja` does
not, so abstract this at the `Env` level instead of wrapping
`ConcreteEnv` in `Arc` ourselves.
* service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone`
* env: add `SYNCS_PER_TX`, `path()`, `shutdown()`
* database: add `src/service/state.rs`
* service: add to `state.rs`, add `DatabaseState` to readers/writer
* add `parking_lot`
Okay, turns out we need to take locks in
`database/src/service/state.rs`...
`std`'s lock fairness policies are not well defined and
depend on the OS implementation, `parking_lot` on the other hand
has a fairness policy which is important when the writer needs
the lock but readers keep pouring in, essentially never letting
the writer do stuff.
* state: use `crossbeam::atomic::AtomicCell`
We have crossbeam as a dep anyway.
* heed: `heed::Env` -> `Arc<RwLock<heed::Env>>`
* service: add reader shutdown handle, use `Select` for msgs
* service: remove `state.rs`
We don't need this, we will represent shutdowns with channel
messages and `Select`, and mutual exclusion with a `RwLock`.
* service: fix misc reader/writer stuff
* database: add `config.rs`
* service: use `ReaderThreads` when spawning readers
* service: impl `shutdown()` for readers/writer
* service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle`
Solves the issue of unfortunately timed `Request`s
that come in _right_ as we are shutting down.
If we (Cuprate) drop the database channels too early the requesting
thread will probably panic as they probably use `.unwrap()`,
never expecting a channel failure.
Returning this structure containing all the channels allows the
final shutdown code to carry these channels until the very end
of the program, at which point, all threads exit - no panics.
* remove `parking_lot`
Could be used as the database mutual exclusion lock.
Needs to be tested against `std`.
* config: add `path`
* env: `path()` -> `config()`, backend: impl `Drop`
* `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect
* backend: add `Config` to `ConcreteEnv`
* service: use `std::thread::Builder` for readers/writer
* config: `PathBuf` -> `Cow<'static, Path>`
* misc docs, remove `RuntimeError::ShuttingDown`
* service: init & shutdown docs
* heed: impl `Env::resize_map()`
* heed: impl `Env::current_map_size()`
* lib.rs: add example crate usage test
* heed: `RwLock` comment
* helper: add `cuprate_database_dir()`
* config: use `cuprate_database_dir()`
* lib.rs: TODO example test
* database: add `page_size`
The database memory map size must be a multiple of
the OS page size. Why doesn't `heed` re-expose this?
It calls it when checking anyway...
https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772
* free: impl `resize_memory_map()`
* free: docs
* env: add `disk_size_bytes()`
* config: impl `From<$num>` for `ReaderThreads`
* move `fs`-related constants to `cuprate_helper::fs`
* docs
* add `resize.rs`, `ResizeAlgorithm`
* env: use `ResizeAlgorithm` in `resize_map()`
* TODO: account for LMDB reader limit
* resize: docs, add `page_size()`, impl `fixed_bytes()`
* resize: impl `percent()`
* resize: docs
* env: take `ResizeAlgorithm` by value (it impls `Copy`)
* heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED`
* config: `From<Into<usize>>` for `ReaderThreads`
Co-authored-by: Boog900 <boog900@tutanota.com>
* env: move mutual exclusion doc to backend
* free: update invariant doc
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/service/mod.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* fix `[allow(unused_imports)] // docs`
* move DB filename from `helper/` -> `database/`
* config: use `DATABASE_FILENAME`
* config: add `db_file_path()`
* lib: add non-`service` usage invariant docs
* table: seal `Table` trait, impl for all `crate::tables`
* fix docs
* fix docs pt.2
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-02-25 19:46:36 +00:00
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
|
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
|
|
|
pub enum ResizeAlgorithm {
|
|
|
|
|
/// Uses [`monero`].
|
|
|
|
|
Monero,
|
|
|
|
|
|
|
|
|
|
/// Uses [`fixed_bytes`].
|
|
|
|
|
FixedBytes(NonZeroUsize),
|
|
|
|
|
|
|
|
|
|
/// Uses [`percent`].
|
|
|
|
|
Percent(f32),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ResizeAlgorithm {
|
|
|
|
|
/// Returns [`Self::Monero`].
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// assert!(matches!(ResizeAlgorithm::new(), ResizeAlgorithm::Monero));
|
|
|
|
|
/// ```
|
|
|
|
|
#[inline]
|
|
|
|
|
pub const fn new() -> Self {
|
|
|
|
|
Self::Monero
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Maps the `self` variant to the free functions in [`crate::resize`].
|
2024-04-16 22:05:38 +00:00
|
|
|
|
///
|
|
|
|
|
/// This function returns the _new_ memory map size in bytes.
|
database: Resizes, Shutdown, Flushing (#68)
* error: add `NeedsResize`
accidently removed, was `MapFull` before.
We need this because we as the writer thread must
react to this error in order to resize.
The writer thread doesn't have access to `heed::Error`, but
`Runtime::Error`, so this variant must exist
* env/backend: add `MANUAL_RESIZE` and `resize()`
* heed: respect `ReadersFull`, comment errors
* free: add `resize_memory_map()`
* env: add `Env::current_map_size`
* service: add `resize_map()`
* database: make `Env` itself cheaply clonable + threadsafe
`heed::Env` already uses `Arc` internally, but `sanakirja` does
not, so abstract this at the `Env` level instead of wrapping
`ConcreteEnv` in `Arc` ourselves.
* service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone`
* env: add `SYNCS_PER_TX`, `path()`, `shutdown()`
* database: add `src/service/state.rs`
* service: add to `state.rs`, add `DatabaseState` to readers/writer
* add `parking_lot`
Okay, turns out we need to take locks in
`database/src/service/state.rs`...
`std`'s lock fairness policies are not well defined and
depend on the OS implementation, `parking_lot` on the other hand
has a fairness policy which is important when the writer needs
the lock but readers keep pouring in, essentially never letting
the writer do stuff.
* state: use `crossbeam::atomic::AtomicCell`
We have crossbeam as a dep anyway.
* heed: `heed::Env` -> `Arc<RwLock<heed::Env>>`
* service: add reader shutdown handle, use `Select` for msgs
* service: remove `state.rs`
We don't need this, we will represent shutdowns with channel
messages and `Select`, and mutual exclusion with a `RwLock`.
* service: fix misc reader/writer stuff
* database: add `config.rs`
* service: use `ReaderThreads` when spawning readers
* service: impl `shutdown()` for readers/writer
* service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle`
Solves the issue of unfortunately timed `Request`s
that come in _right_ as we are shutting down.
If we (Cuprate) drop the database channels too early the requesting
thread will probably panic as they probably use `.unwrap()`,
never expecting a channel failure.
Returning this structure containing all the channels allows the
final shutdown code to carry these channels until the very end
of the program, at which point, all threads exit - no panics.
* remove `parking_lot`
Could be used as the database mutual exclusion lock.
Needs to be tested against `std`.
* config: add `path`
* env: `path()` -> `config()`, backend: impl `Drop`
* `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect
* backend: add `Config` to `ConcreteEnv`
* service: use `std::thread::Builder` for readers/writer
* config: `PathBuf` -> `Cow<'static, Path>`
* misc docs, remove `RuntimeError::ShuttingDown`
* service: init & shutdown docs
* heed: impl `Env::resize_map()`
* heed: impl `Env::current_map_size()`
* lib.rs: add example crate usage test
* heed: `RwLock` comment
* helper: add `cuprate_database_dir()`
* config: use `cuprate_database_dir()`
* lib.rs: TODO example test
* database: add `page_size`
The database memory map size must be a multiple of
the OS page size. Why doesn't `heed` re-expose this?
It calls it when checking anyway...
https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772
* free: impl `resize_memory_map()`
* free: docs
* env: add `disk_size_bytes()`
* config: impl `From<$num>` for `ReaderThreads`
* move `fs`-related constants to `cuprate_helper::fs`
* docs
* add `resize.rs`, `ResizeAlgorithm`
* env: use `ResizeAlgorithm` in `resize_map()`
* TODO: account for LMDB reader limit
* resize: docs, add `page_size()`, impl `fixed_bytes()`
* resize: impl `percent()`
* resize: docs
* env: take `ResizeAlgorithm` by value (it impls `Copy`)
* heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED`
* config: `From<Into<usize>>` for `ReaderThreads`
Co-authored-by: Boog900 <boog900@tutanota.com>
* env: move mutual exclusion doc to backend
* free: update invariant doc
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/service/mod.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* fix `[allow(unused_imports)] // docs`
* move DB filename from `helper/` -> `database/`
* config: use `DATABASE_FILENAME`
* config: add `db_file_path()`
* lib: add non-`service` usage invariant docs
* table: seal `Table` trait, impl for all `crate::tables`
* fix docs
* fix docs pt.2
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-02-25 19:46:36 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn resize(&self, current_size_bytes: usize) -> NonZeroUsize {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Monero => monero(current_size_bytes),
|
database: impl trait function bodies for `heed` & `redb` (#85)
* env: remove `T: Table` for `Env::create_tables()`
It creates _all_ tables, not a specific `T: Table`
* heed: half impl `Env::open()`, some TODOs
* heed: add `HeedTxR{o,w}<'env>`
* workspace/cargo: add `parking_lot`
* remove `parking_lot`
`MappedRwLockGuard` doesn't solve the `returning reference to
object owned by function` error when returning heed's lock guard
+ the tx so we'll be going with `std`
* env: add `{EnvInner,TxRoInput,TxRwInput}` and getter `fn()`s
* env: fix tx <-> table lifetimes, fix `Env::create_tables()`
* heed: impl `DatabaseRo`
* heed: impl `DatabaseRw`
* database: add `src/backend/${BACKEND}/tests.rs`
* heed: impl more of `Env::open()`
* redb: fix trait signatures, add `trait ValueGuard`
* accommodate `DatabaseRo` bounds for both `{heed,redb}`
* fold `get_range()` generic + bounds
* add `TxCreator`, add `heed` tests
* env: `TxCreator` -> `EnvInner` which doubles as DB/Table opener
* database: `DatabaseRw<'tx>` -> `DatabaseRw<'db, 'tx>`
* heed: `db_read_write()` test
* database: fix `get()` lifetimes, heed: add `db_read_write()` test
* database: remove `'env` lifetime from `DatabaseRo<'env, 'tx>`
not needed for immutable references
* redb: fix new `{Env, EnvInner, DatabaseR{o,w}}` bounds
* redb: impl `Database` traits
* redb: impl `TxR{o,w}` traits
* redb: impl `Env`
* redb: open/create tables in `Env::open`
* redb: enable tests, add tmp `Storable` printlns
* redb: fix alignment issue with `Cow` and `from_bytes_unaligned()`
* redb: only allocate bytes when alignment != 1
* heed: remove custom iterator from `range()`
* storable: conditionally allocat on misaligned bytes in `from_bytes`
* add database guard
* database: AccessGuard -> ValueGuard
* storable: add `ALIGN` and `from_bytes_unaligned()`
* redb: 1 serde type `StorableRedb`, fix impl
* cow serde, trait bounds, fix backend's where bounds
- Uses Cow for `redb`'s deserialization
- Conforms `heed` to use Cow (but not as the actual key/value)
- Conforms the `cuprate_database` trait API to use Cow
- Adds `ToOwned + Debug` (and permutation) trait bounds
- Solves 23098573148968945687345349657398 compiler errors due
to aforementioned trait bounds, causing `where` to be everywhere
* fix docs, use fully qualified Tx functions for tests
* backend: check value guard contains value in test
* add `Storable::ALIGN` tests, doc TODOs
* add `trait ToOwnedDebug`
* traits: `ToOwned + Debug` -> `ToOwnedDebug`
* heed: remove `ToOwned` + `Debug` bounds
* redb: remove `ToOwned` + `Debug` bounds
* add `ValueGuard`, replace signatures, fix `redb`
* heed: fix for `ValueGuard`
* docs, tests
* redb: add `CowRange` for `T::Key` -> `Cow<'_, T::Key>` conversion
* separate `config.rs` -> `config/`
* ci: combine tests, run both `heed` and `redb` tests
* ci: fix workflow
* backend: add `resize()` test
* ci: remove windows-specific update
* ci: remove update + windows default set
* backend: add `unreachable` tests, fix docs
* trait docs
* ci: fix
* Update database/src/backend/heed/env.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/heed/env.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/heed/transaction.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/heed/transaction.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/heed/transaction.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/redb/database.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/redb/database.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/backend/heed/database.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* readme: fix `value_guard.rs`
* heed: remove unneeded clippy + fix formatting
* heed: create and use `create_table()` in `Env::open()`
* redb: create and use `create_table()` in `Env::open()`
* redb: remove unneeded clippy
* fix clippy, remove `<[u8], [u8]>` docs
---------
Co-authored-by: Boog900 <54e72d8a-345f-4599-bd90-c6b9bc7d0ec5@aleeas.com>
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-03-13 22:05:24 +00:00
|
|
|
|
Self::FixedBytes(add_bytes) => fixed_bytes(current_size_bytes, add_bytes.get()),
|
|
|
|
|
Self::Percent(f) => percent(current_size_bytes, *f),
|
database: Resizes, Shutdown, Flushing (#68)
* error: add `NeedsResize`
accidently removed, was `MapFull` before.
We need this because we as the writer thread must
react to this error in order to resize.
The writer thread doesn't have access to `heed::Error`, but
`Runtime::Error`, so this variant must exist
* env/backend: add `MANUAL_RESIZE` and `resize()`
* heed: respect `ReadersFull`, comment errors
* free: add `resize_memory_map()`
* env: add `Env::current_map_size`
* service: add `resize_map()`
* database: make `Env` itself cheaply clonable + threadsafe
`heed::Env` already uses `Arc` internally, but `sanakirja` does
not, so abstract this at the `Env` level instead of wrapping
`ConcreteEnv` in `Arc` ourselves.
* service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone`
* env: add `SYNCS_PER_TX`, `path()`, `shutdown()`
* database: add `src/service/state.rs`
* service: add to `state.rs`, add `DatabaseState` to readers/writer
* add `parking_lot`
Okay, turns out we need to take locks in
`database/src/service/state.rs`...
`std`'s lock fairness policies are not well defined and
depend on the OS implementation, `parking_lot` on the other hand
has a fairness policy which is important when the writer needs
the lock but readers keep pouring in, essentially never letting
the writer do stuff.
* state: use `crossbeam::atomic::AtomicCell`
We have crossbeam as a dep anyway.
* heed: `heed::Env` -> `Arc<RwLock<heed::Env>>`
* service: add reader shutdown handle, use `Select` for msgs
* service: remove `state.rs`
We don't need this, we will represent shutdowns with channel
messages and `Select`, and mutual exclusion with a `RwLock`.
* service: fix misc reader/writer stuff
* database: add `config.rs`
* service: use `ReaderThreads` when spawning readers
* service: impl `shutdown()` for readers/writer
* service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle`
Solves the issue of unfortunately timed `Request`s
that come in _right_ as we are shutting down.
If we (Cuprate) drop the database channels too early the requesting
thread will probably panic as they probably use `.unwrap()`,
never expecting a channel failure.
Returning this structure containing all the channels allows the
final shutdown code to carry these channels until the very end
of the program, at which point, all threads exit - no panics.
* remove `parking_lot`
Could be used as the database mutual exclusion lock.
Needs to be tested against `std`.
* config: add `path`
* env: `path()` -> `config()`, backend: impl `Drop`
* `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect
* backend: add `Config` to `ConcreteEnv`
* service: use `std::thread::Builder` for readers/writer
* config: `PathBuf` -> `Cow<'static, Path>`
* misc docs, remove `RuntimeError::ShuttingDown`
* service: init & shutdown docs
* heed: impl `Env::resize_map()`
* heed: impl `Env::current_map_size()`
* lib.rs: add example crate usage test
* heed: `RwLock` comment
* helper: add `cuprate_database_dir()`
* config: use `cuprate_database_dir()`
* lib.rs: TODO example test
* database: add `page_size`
The database memory map size must be a multiple of
the OS page size. Why doesn't `heed` re-expose this?
It calls it when checking anyway...
https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772
* free: impl `resize_memory_map()`
* free: docs
* env: add `disk_size_bytes()`
* config: impl `From<$num>` for `ReaderThreads`
* move `fs`-related constants to `cuprate_helper::fs`
* docs
* add `resize.rs`, `ResizeAlgorithm`
* env: use `ResizeAlgorithm` in `resize_map()`
* TODO: account for LMDB reader limit
* resize: docs, add `page_size()`, impl `fixed_bytes()`
* resize: impl `percent()`
* resize: docs
* env: take `ResizeAlgorithm` by value (it impls `Copy`)
* heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED`
* config: `From<Into<usize>>` for `ReaderThreads`
Co-authored-by: Boog900 <boog900@tutanota.com>
* env: move mutual exclusion doc to backend
* free: update invariant doc
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/service/mod.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* fix `[allow(unused_imports)] // docs`
* move DB filename from `helper/` -> `database/`
* config: use `DATABASE_FILENAME`
* config: add `db_file_path()`
* lib: add non-`service` usage invariant docs
* table: seal `Table` trait, impl for all `crate::tables`
* fix docs
* fix docs pt.2
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-02-25 19:46:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ResizeAlgorithm {
|
|
|
|
|
/// Calls [`Self::new`].
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// assert_eq!(ResizeAlgorithm::new(), ResizeAlgorithm::default());
|
|
|
|
|
/// ```
|
|
|
|
|
#[inline]
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- Free functions
|
|
|
|
|
/// This function retrieves the system’s memory page size.
|
|
|
|
|
///
|
|
|
|
|
/// It is just [`page_size::get`](https://docs.rs/page_size) internally.
|
|
|
|
|
///
|
|
|
|
|
/// This caches the result, so this function is cheap after the 1st call.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
/// This function will panic if the OS returns of page size of `0` (impossible?).
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn page_size() -> NonZeroUsize {
|
2024-05-24 01:16:39 +00:00
|
|
|
|
/// Cached result of [`page_size()`].
|
|
|
|
|
static PAGE_SIZE: OnceLock<NonZeroUsize> = OnceLock::new();
|
database: Resizes, Shutdown, Flushing (#68)
* error: add `NeedsResize`
accidently removed, was `MapFull` before.
We need this because we as the writer thread must
react to this error in order to resize.
The writer thread doesn't have access to `heed::Error`, but
`Runtime::Error`, so this variant must exist
* env/backend: add `MANUAL_RESIZE` and `resize()`
* heed: respect `ReadersFull`, comment errors
* free: add `resize_memory_map()`
* env: add `Env::current_map_size`
* service: add `resize_map()`
* database: make `Env` itself cheaply clonable + threadsafe
`heed::Env` already uses `Arc` internally, but `sanakirja` does
not, so abstract this at the `Env` level instead of wrapping
`ConcreteEnv` in `Arc` ourselves.
* service: `Arc<ConcreteEnv>` -> `ConcreteEnv: Clone`
* env: add `SYNCS_PER_TX`, `path()`, `shutdown()`
* database: add `src/service/state.rs`
* service: add to `state.rs`, add `DatabaseState` to readers/writer
* add `parking_lot`
Okay, turns out we need to take locks in
`database/src/service/state.rs`...
`std`'s lock fairness policies are not well defined and
depend on the OS implementation, `parking_lot` on the other hand
has a fairness policy which is important when the writer needs
the lock but readers keep pouring in, essentially never letting
the writer do stuff.
* state: use `crossbeam::atomic::AtomicCell`
We have crossbeam as a dep anyway.
* heed: `heed::Env` -> `Arc<RwLock<heed::Env>>`
* service: add reader shutdown handle, use `Select` for msgs
* service: remove `state.rs`
We don't need this, we will represent shutdowns with channel
messages and `Select`, and mutual exclusion with a `RwLock`.
* service: fix misc reader/writer stuff
* database: add `config.rs`
* service: use `ReaderThreads` when spawning readers
* service: impl `shutdown()` for readers/writer
* service: return `DatabaseReaderReceivers` on shutdown via `JoinHandle`
Solves the issue of unfortunately timed `Request`s
that come in _right_ as we are shutting down.
If we (Cuprate) drop the database channels too early the requesting
thread will probably panic as they probably use `.unwrap()`,
never expecting a channel failure.
Returning this structure containing all the channels allows the
final shutdown code to carry these channels until the very end
of the program, at which point, all threads exit - no panics.
* remove `parking_lot`
Could be used as the database mutual exclusion lock.
Needs to be tested against `std`.
* config: add `path`
* env: `path()` -> `config()`, backend: impl `Drop`
* `Arc<ConcreteEnv>`, shutdown `service` on channel disconnect
* backend: add `Config` to `ConcreteEnv`
* service: use `std::thread::Builder` for readers/writer
* config: `PathBuf` -> `Cow<'static, Path>`
* misc docs, remove `RuntimeError::ShuttingDown`
* service: init & shutdown docs
* heed: impl `Env::resize_map()`
* heed: impl `Env::current_map_size()`
* lib.rs: add example crate usage test
* heed: `RwLock` comment
* helper: add `cuprate_database_dir()`
* config: use `cuprate_database_dir()`
* lib.rs: TODO example test
* database: add `page_size`
The database memory map size must be a multiple of
the OS page size. Why doesn't `heed` re-expose this?
It calls it when checking anyway...
https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#772
* free: impl `resize_memory_map()`
* free: docs
* env: add `disk_size_bytes()`
* config: impl `From<$num>` for `ReaderThreads`
* move `fs`-related constants to `cuprate_helper::fs`
* docs
* add `resize.rs`, `ResizeAlgorithm`
* env: use `ResizeAlgorithm` in `resize_map()`
* TODO: account for LMDB reader limit
* resize: docs, add `page_size()`, impl `fixed_bytes()`
* resize: impl `percent()`
* resize: docs
* env: take `ResizeAlgorithm` by value (it impls `Copy`)
* heed: TODO for `MDB_MAP_FULL` & `MDB_MAP_RESIZED`
* config: `From<Into<usize>>` for `ReaderThreads`
Co-authored-by: Boog900 <boog900@tutanota.com>
* env: move mutual exclusion doc to backend
* free: update invariant doc
Co-authored-by: Boog900 <boog900@tutanota.com>
* Update database/src/service/mod.rs
Co-authored-by: Boog900 <boog900@tutanota.com>
* fix `[allow(unused_imports)] // docs`
* move DB filename from `helper/` -> `database/`
* config: use `DATABASE_FILENAME`
* config: add `db_file_path()`
* lib: add non-`service` usage invariant docs
* table: seal `Table` trait, impl for all `crate::tables`
* fix docs
* fix docs pt.2
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
2024-02-25 19:46:36 +00:00
|
|
|
|
*PAGE_SIZE
|
|
|
|
|
.get_or_init(|| NonZeroUsize::new(page_size::get()).expect("page_size::get() returned 0"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Memory map resize closely matching `monerod`.
|
|
|
|
|
///
|
|
|
|
|
/// # Method
|
|
|
|
|
/// This function mostly matches `monerod`'s current resize implementation[^1],
|
|
|
|
|
/// and will increase `current_size_bytes` by `1 << 30`[^2] exactly then
|
|
|
|
|
/// rounded to the nearest multiple of the OS page size.
|
|
|
|
|
///
|
|
|
|
|
/// [^1]: <https://github.com/monero-project/monero/blob/059028a30a8ae9752338a7897329fe8012a310d5/src/blockchain_db/lmdb/db_lmdb.cpp#L549>
|
|
|
|
|
///
|
|
|
|
|
/// [^2]: `1_073_745_920`
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// // The value this function will increment by
|
|
|
|
|
/// // (assuming page multiple of 4096).
|
|
|
|
|
/// const N: usize = 1_073_741_824;
|
|
|
|
|
///
|
|
|
|
|
/// // 0 returns the minimum value.
|
|
|
|
|
/// assert_eq!(monero(0).get(), N);
|
|
|
|
|
///
|
|
|
|
|
/// // Rounds up to nearest OS page size.
|
|
|
|
|
/// assert_eq!(monero(1).get(), N + page_size().get());
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
/// This function will panic if adding onto `current_size_bytes` overflows [`usize::MAX`].
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,should_panic
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// // Ridiculous large numbers panic.
|
|
|
|
|
/// monero(usize::MAX);
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn monero(current_size_bytes: usize) -> NonZeroUsize {
|
|
|
|
|
/// The exact expression used by `monerod`
|
|
|
|
|
/// when calculating how many bytes to add.
|
|
|
|
|
///
|
|
|
|
|
/// The nominal value is `1_073_741_824`.
|
|
|
|
|
/// Not actually 1 GB but close enough I guess.
|
|
|
|
|
///
|
|
|
|
|
/// <https://github.com/monero-project/monero/blob/059028a30a8ae9752338a7897329fe8012a310d5/src/blockchain_db/lmdb/db_lmdb.cpp#L553>
|
|
|
|
|
const ADD_SIZE: usize = 1_usize << 30;
|
|
|
|
|
|
|
|
|
|
let page_size = page_size().get();
|
|
|
|
|
let new_size_bytes = current_size_bytes + ADD_SIZE;
|
|
|
|
|
|
|
|
|
|
// Round up the new size to the
|
|
|
|
|
// nearest multiple of the OS page size.
|
|
|
|
|
let remainder = new_size_bytes % page_size;
|
|
|
|
|
|
|
|
|
|
// INVARIANT: minimum is always at least `ADD_SIZE`.
|
|
|
|
|
NonZeroUsize::new(if remainder == 0 {
|
|
|
|
|
new_size_bytes
|
|
|
|
|
} else {
|
|
|
|
|
(new_size_bytes + page_size) - remainder
|
|
|
|
|
})
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Memory map resize by a fixed amount of bytes.
|
|
|
|
|
///
|
|
|
|
|
/// # Method
|
|
|
|
|
/// This function will `current_size_bytes + add_bytes`
|
|
|
|
|
/// and then round up to nearest OS page size.
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// let page_size: usize = page_size().get();
|
|
|
|
|
///
|
|
|
|
|
/// // Anything below the page size will round up to the page size.
|
|
|
|
|
/// for i in 0..=page_size {
|
|
|
|
|
/// assert_eq!(fixed_bytes(0, i).get(), page_size);
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// // (page_size + 1) will round up to (page_size * 2).
|
|
|
|
|
/// assert_eq!(fixed_bytes(page_size, 1).get(), page_size * 2);
|
|
|
|
|
///
|
|
|
|
|
/// // (page_size + page_size) doesn't require any rounding.
|
|
|
|
|
/// assert_eq!(fixed_bytes(page_size, page_size).get(), page_size * 2);
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
/// This function will panic if adding onto `current_size_bytes` overflows [`usize::MAX`].
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,should_panic
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// // Ridiculous large numbers panic.
|
|
|
|
|
/// fixed_bytes(1, usize::MAX);
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn fixed_bytes(current_size_bytes: usize, add_bytes: usize) -> NonZeroUsize {
|
|
|
|
|
let page_size = page_size();
|
|
|
|
|
let new_size_bytes = current_size_bytes + add_bytes;
|
|
|
|
|
|
|
|
|
|
// Guard against < page_size.
|
|
|
|
|
if new_size_bytes <= page_size.get() {
|
|
|
|
|
return page_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Round up the new size to the
|
|
|
|
|
// nearest multiple of the OS page size.
|
|
|
|
|
let remainder = new_size_bytes % page_size;
|
|
|
|
|
|
|
|
|
|
// INVARIANT: we guarded against < page_size above.
|
|
|
|
|
NonZeroUsize::new(if remainder == 0 {
|
|
|
|
|
new_size_bytes
|
|
|
|
|
} else {
|
|
|
|
|
(new_size_bytes + page_size.get()) - remainder
|
|
|
|
|
})
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Memory map resize by a percentage.
|
|
|
|
|
///
|
|
|
|
|
/// # Method
|
|
|
|
|
/// This function will multiply `current_size_bytes` by `percent`.
|
|
|
|
|
///
|
|
|
|
|
/// Any input `<= 1.0` or non-normal float ([`f32::NAN`], [`f32::INFINITY`])
|
|
|
|
|
/// will make the returning `NonZeroUsize` the same as `current_size_bytes`
|
|
|
|
|
/// (rounded up to the OS page size).
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// let page_size: usize = page_size().get();
|
|
|
|
|
///
|
|
|
|
|
/// // Anything below the page size will round up to the page size.
|
|
|
|
|
/// for i in 0..=page_size {
|
|
|
|
|
/// assert_eq!(percent(i, 1.0).get(), page_size);
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// // Same for 2 page sizes.
|
|
|
|
|
/// for i in (page_size + 1)..=(page_size * 2) {
|
|
|
|
|
/// assert_eq!(percent(i, 1.0).get(), page_size * 2);
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// // Weird floats do nothing.
|
|
|
|
|
/// assert_eq!(percent(page_size, f32::NAN).get(), page_size);
|
|
|
|
|
/// assert_eq!(percent(page_size, f32::INFINITY).get(), page_size);
|
|
|
|
|
/// assert_eq!(percent(page_size, f32::NEG_INFINITY).get(), page_size);
|
|
|
|
|
/// assert_eq!(percent(page_size, -1.0).get(), page_size);
|
|
|
|
|
/// assert_eq!(percent(page_size, 0.999).get(), page_size);
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
/// This function will panic if `current_size_bytes * percent`
|
|
|
|
|
/// is closer to [`usize::MAX`] than the OS page size.
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,should_panic
|
|
|
|
|
/// # use cuprate_database::resize::*;
|
|
|
|
|
/// // Ridiculous large numbers panic.
|
|
|
|
|
/// percent(usize::MAX, 1.001);
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn percent(current_size_bytes: usize, percent: f32) -> NonZeroUsize {
|
|
|
|
|
// Guard against bad floats.
|
|
|
|
|
use std::num::FpCategory;
|
|
|
|
|
let percent = match percent.classify() {
|
|
|
|
|
FpCategory::Normal => {
|
|
|
|
|
if percent <= 1.0 {
|
|
|
|
|
1.0
|
|
|
|
|
} else {
|
|
|
|
|
percent
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => 1.0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let page_size = page_size();
|
|
|
|
|
|
|
|
|
|
// INVARIANT: Allow `f32` <-> `usize` casting, we handle all cases.
|
|
|
|
|
#[allow(
|
|
|
|
|
clippy::cast_possible_truncation,
|
|
|
|
|
clippy::cast_sign_loss,
|
|
|
|
|
clippy::cast_precision_loss
|
|
|
|
|
)]
|
|
|
|
|
let new_size_bytes = ((current_size_bytes as f32) * percent) as usize;
|
|
|
|
|
|
|
|
|
|
// Panic if rounding up to the nearest page size would overflow.
|
|
|
|
|
let new_size_bytes = if new_size_bytes > (usize::MAX - page_size.get()) {
|
|
|
|
|
panic!("new_size_bytes is percent() near usize::MAX");
|
|
|
|
|
} else {
|
|
|
|
|
new_size_bytes
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Guard against < page_size.
|
|
|
|
|
if new_size_bytes <= page_size.get() {
|
|
|
|
|
return page_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Round up the new size to the
|
|
|
|
|
// nearest multiple of the OS page size.
|
|
|
|
|
let remainder = new_size_bytes % page_size;
|
|
|
|
|
|
|
|
|
|
// INVARIANT: we guarded against < page_size above.
|
|
|
|
|
NonZeroUsize::new(if remainder == 0 {
|
|
|
|
|
new_size_bytes
|
|
|
|
|
} else {
|
|
|
|
|
(new_size_bytes + page_size.get()) - remainder
|
|
|
|
|
})
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- Tests
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
|
|
|
|
// use super::*;
|
|
|
|
|
}
|