From 10538573aa408c4e32141e93c0c3532c78b336c7 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 17:10:08 -0400 Subject: [PATCH 01/25] add `cuprate-types` --- Cargo.lock | 1 + database/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index d9248e2b5..7c0750fe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,6 +596,7 @@ dependencies = [ "cfg-if", "crossbeam", "cuprate-helper", + "cuprate-types", "futures", "heed", "page_size", diff --git a/database/Cargo.toml b/database/Cargo.toml index 33c3937c5..646b76bf6 100644 --- a/database/Cargo.toml +++ b/database/Cargo.toml @@ -25,6 +25,7 @@ cfg-if = { workspace = true } # We only need the `thread` feature if `service` is enabled. # Figure out how to enable features of an already pulled in dependency conditionally. cuprate-helper = { path = "../helper", features = ["fs", "thread"] } +cuprate-types = { path = "../types", features = ["service"] } paste = { workspace = true } page_size = { version = "0.6.0" } # Needed for database resizes, they must be a multiple of the OS page size. thiserror = { workspace = true } From 56110d51b3845eada2b6047c1e4532f2d8b095fd Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 17:10:34 -0400 Subject: [PATCH 02/25] remove `cuprate_database::service::{request,response}` --- database/src/service/request.rs | 41 -------------------------------- database/src/service/response.rs | 38 ----------------------------- 2 files changed, 79 deletions(-) delete mode 100644 database/src/service/request.rs delete mode 100644 database/src/service/response.rs diff --git a/database/src/service/request.rs b/database/src/service/request.rs deleted file mode 100644 index 93877ecde..000000000 --- a/database/src/service/request.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Read/write `Request`s to the database. -//! -//! TODO: could add `strum` derives. - -//---------------------------------------------------------------------------------------------------- Import - -//---------------------------------------------------------------------------------------------------- Constants - -//---------------------------------------------------------------------------------------------------- ReadRequest -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// A read request to the database. -pub enum ReadRequest { - /// TODO - Example1, - /// TODO - Example2(usize), - /// TODO - Example3(String), -} - -//---------------------------------------------------------------------------------------------------- WriteRequest -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// A write request to the database. -pub enum WriteRequest { - /// TODO - Example1, - /// TODO - Example2(usize), - /// TODO - Example3(String), -} - -//---------------------------------------------------------------------------------------------------- IMPL - -//---------------------------------------------------------------------------------------------------- Trait Impl - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -} diff --git a/database/src/service/response.rs b/database/src/service/response.rs deleted file mode 100644 index 6977efdb4..000000000 --- a/database/src/service/response.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Read/write `Response`'s from the database. -//! -//! TODO: could add `strum` derives. - -//---------------------------------------------------------------------------------------------------- Import - -//---------------------------------------------------------------------------------------------------- Constants - -//---------------------------------------------------------------------------------------------------- Response -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// A response from the database. -/// -/// TODO -pub enum Response { - //-------------------------------------------------------- Read responses - /// TODO - Example1, - /// TODO - Example2(usize), - /// TODO - Example3(String), - - //-------------------------------------------------------- Write responses - /// The response - /// - /// TODO - ExampleWriteResponse, // Probably will be just `Ok` -} - -//---------------------------------------------------------------------------------------------------- IMPL - -//---------------------------------------------------------------------------------------------------- Trait Impl - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -} From 147be97496aedb4ba952936f29dff7ba21a73453 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 17:10:53 -0400 Subject: [PATCH 03/25] use `cuprate_types::service::{request,response}` --- database/src/lib.rs | 2 + database/src/service/mod.rs | 20 +++++---- database/src/service/read.rs | 40 +++--------------- database/src/service/tests.rs | 78 ++++++++++++++++++----------------- database/src/service/write.rs | 39 +++--------------- 5 files changed, 63 insertions(+), 116 deletions(-) diff --git a/database/src/lib.rs b/database/src/lib.rs index 51315f20f..efcf6eef2 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -100,6 +100,8 @@ //! config::Config, //! ConcreteEnv, //! Env, Key, TxRo, TxRw, +//! }; +//! use cuprate_types::{ //! service::{ReadRequest, WriteRequest, Response}, //! }; //! diff --git a/database/src/service/mod.rs b/database/src/service/mod.rs index 83f8088ad..ab4873fa6 100644 --- a/database/src/service/mod.rs +++ b/database/src/service/mod.rs @@ -5,7 +5,7 @@ //! along with the reader/writer thread-pool system. //! //! The thread-pool allows outside crates to communicate with it by -//! sending database [`Request`](ReadRequest)s and receiving [`Response`]s `async`hronously - +//! sending database [`Request`][req_r]s and receiving [`Response`][resp]s `async`hronously - //! without having to actually worry and handle the database themselves. //! //! The system is managed by this crate, and only requires [`init`] by the user. @@ -17,9 +17,9 @@ //! - [`DatabaseReadHandle`] //! - [`DatabaseWriteHandle`] //! -//! The 1st allows any caller to send [`ReadRequest`]s. +//! The 1st allows any caller to send [`ReadRequest`][req_r]s. //! -//! The 2nd allows any caller to send [`WriteRequest`]s. +//! The 2nd allows any caller to send [`WriteRequest`][req_w]s. //! //! The `DatabaseReadHandle` can be shared as it is cheaply [`Clone`]able, however, //! the `DatabaseWriteHandle` cannot be cloned. There is only 1 place in Cuprate that @@ -49,6 +49,14 @@ //! An `async`hronous channel will be returned from the call. //! This channel can be `.await`ed upon to (eventually) receive //! the corresponding `Response` to your `Request`. +//! +//! +//! +//! [req_r]: cuprate_types::service::ReadRequest +//! +//! [req_w]: cuprate_types::service::WriteRequest +//! +//! [resp]: cuprate_types::service::Response mod read; pub use read::DatabaseReadHandle; @@ -59,11 +67,5 @@ pub use write::DatabaseWriteHandle; mod free; pub use free::init; -mod request; -pub use request::{ReadRequest, WriteRequest}; - -mod response; -pub use response::Response; - #[cfg(test)] mod tests; diff --git a/database/src/service/read.rs b/database/src/service/read.rs index 7361ce729..c89bd3c5d 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -14,13 +14,9 @@ use tokio::sync::{OwnedSemaphorePermit, Semaphore}; use tokio_util::sync::PollSemaphore; use cuprate_helper::asynch::InfallibleOneshotReceiver; +use cuprate_types::service::{ReadRequest, Response}; -use crate::{ - config::ReaderThreads, - error::RuntimeError, - service::{request::ReadRequest, response::Response}, - ConcreteEnv, -}; +use crate::{config::ReaderThreads, error::RuntimeError, ConcreteEnv}; //---------------------------------------------------------------------------------------------------- Types /// The actual type of the response. @@ -211,11 +207,9 @@ fn map_request( ) { /* TODO: pre-request handling, run some code for each request? */ - match request { - ReadRequest::Example1 => example_handler_1(env, response_sender), - ReadRequest::Example2(x) => example_handler_2(env, response_sender, x), - ReadRequest::Example3(x) => example_handler_3(env, response_sender, x), - } + // match request { + // } + todo!() /* TODO: post-request handling, run some code for each request? */ } @@ -230,27 +224,3 @@ fn map_request( // // All functions below assume that this is the case, such that // `par_*()` functions will not block the _global_ rayon thread-pool. - -/// TODO -#[inline] -#[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn example_handler_1(env: Arc, response_sender: ResponseSender) { - let db_result = Ok(Response::Example1); - response_sender.send(db_result).unwrap(); -} - -/// TODO -#[inline] -#[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn example_handler_2(env: Arc, response_sender: ResponseSender, x: usize) { - let db_result = Ok(Response::Example2(x)); - response_sender.send(db_result).unwrap(); -} - -/// TODO -#[inline] -#[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn example_handler_3(env: Arc, response_sender: ResponseSender, x: String) { - let db_result = Ok(Response::Example3(x)); - response_sender.send(db_result).unwrap(); -} diff --git a/database/src/service/tests.rs b/database/src/service/tests.rs index a01f67a51..8f80bfc64 100644 --- a/database/src/service/tests.rs +++ b/database/src/service/tests.rs @@ -12,9 +12,11 @@ //---------------------------------------------------------------------------------------------------- Use use tower::{Service, ServiceExt}; +use cuprate_types::service::{ReadRequest, Response, WriteRequest}; + use crate::{ config::Config, - service::{init, DatabaseReadHandle, DatabaseWriteHandle, ReadRequest, Response, WriteRequest}, + service::{init, DatabaseReadHandle, DatabaseWriteHandle}, }; //---------------------------------------------------------------------------------------------------- Tests @@ -34,43 +36,43 @@ fn init_drop() { let (reader, writer, _tempdir) = init_service(); } -/// Send a read request, and receive a response, -/// asserting the response the expected value. -#[tokio::test] -async fn read_request() { - let (reader, writer, _tempdir) = init_service(); +// /// Send a read request, and receive a response, +// /// asserting the response the expected value. +// #[tokio::test] +// async fn read_request() { +// let (reader, writer, _tempdir) = init_service(); - for (request, expected_response) in [ - (ReadRequest::Example1, Response::Example1), - (ReadRequest::Example2(123), Response::Example2(123)), - ( - ReadRequest::Example3("hello".into()), - Response::Example3("hello".into()), - ), - ] { - // This calls `poll_ready()` asserting we have a permit before `call()`. - let response_channel = reader.clone().oneshot(request); - let response = response_channel.await.unwrap(); - assert_eq!(response, expected_response); - } -} +// for (request, expected_response) in [ +// (ReadRequest::Example1, Response::Example1), +// (ReadRequest::Example2(123), Response::Example2(123)), +// ( +// ReadRequest::Example3("hello".into()), +// Response::Example3("hello".into()), +// ), +// ] { +// // This calls `poll_ready()` asserting we have a permit before `call()`. +// let response_channel = reader.clone().oneshot(request); +// let response = response_channel.await.unwrap(); +// assert_eq!(response, expected_response); +// } +// } -/// Send a write request, and receive a response, -/// asserting the response the expected value. -#[tokio::test] -async fn write_request() { - let (reader, mut writer, _tempdir) = init_service(); +// /// Send a write request, and receive a response, +// /// asserting the response the expected value. +// #[tokio::test] +// async fn write_request() { +// let (reader, mut writer, _tempdir) = init_service(); - for (request, expected_response) in [ - (WriteRequest::Example1, Response::Example1), - (WriteRequest::Example2(123), Response::Example2(123)), - ( - WriteRequest::Example3("hello".into()), - Response::Example3("hello".into()), - ), - ] { - let response_channel = writer.call(request); - let response = response_channel.await.unwrap(); - assert_eq!(response, expected_response); - } -} +// for (request, expected_response) in [ +// (WriteRequest::Example1, Response::Example1), +// (WriteRequest::Example2(123), Response::Example2(123)), +// ( +// WriteRequest::Example3("hello".into()), +// Response::Example3("hello".into()), +// ), +// ] { +// let response_channel = writer.call(request); +// let response = response_channel.await.unwrap(); +// assert_eq!(response, expected_response); +// } +// } diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 13e6f9791..fbb67b271 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -9,12 +9,9 @@ use std::{ use futures::channel::oneshot; use cuprate_helper::asynch::InfallibleOneshotReceiver; +use cuprate_types::service::{Response, WriteRequest}; -use crate::{ - error::RuntimeError, - service::{request::WriteRequest, response::Response}, - ConcreteEnv, Env, -}; +use crate::{error::RuntimeError, ConcreteEnv, Env}; //---------------------------------------------------------------------------------------------------- Constants /// Name of the writer thread. @@ -141,11 +138,9 @@ impl DatabaseWriter { }; // Map [`Request`]'s to specific database functions. - match request { - WriteRequest::Example1 => self.example_handler_1(response_sender), - WriteRequest::Example2(x) => self.example_handler_2(response_sender, x), - WriteRequest::Example3(x) => self.example_handler_3(response_sender, x), - } + // match request { + // } + todo!() } } @@ -172,28 +167,4 @@ impl DatabaseWriter { // of data, add that much instead of the default 1GB. // } - - /// TODO - #[inline] - #[allow(clippy::unused_self)] // TODO: remove me - fn example_handler_1(&self, response_sender: ResponseSender) { - let db_result = Ok(Response::Example1); - response_sender.send(db_result).unwrap(); - } - - /// TODO - #[inline] - #[allow(clippy::unused_self)] // TODO: remove me - fn example_handler_2(&self, response_sender: ResponseSender, x: usize) { - let db_result = Ok(Response::Example2(x)); - response_sender.send(db_result).unwrap(); - } - - /// TODO - #[inline] - #[allow(clippy::unused_self)] // TODO: remove me - fn example_handler_3(&self, response_sender: ResponseSender, x: String) { - let db_result = Ok(Response::Example3(x)); - response_sender.send(db_result).unwrap(); - } } From b8dad114db804e40258de052901558a475295747 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 20:28:21 -0400 Subject: [PATCH 04/25] service: fix `Request` `match`'s --- database/src/service/read.rs | 17 +++++++++++++---- database/src/service/tests.rs | 4 ++++ database/src/service/write.rs | 8 +++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index c89bd3c5d..68cf46491 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -206,10 +206,19 @@ fn map_request( response_sender: ResponseSender, // The channel we must send the response back to ) { /* TODO: pre-request handling, run some code for each request? */ - - // match request { - // } - todo!() + use ReadRequest as R; + + match request { + R::BlockExtendedHeader(block) => todo!(), + R::BlockHash(block) => todo!(), + R::BlockExtendedHeaderInRange(range) => todo!(), + R::ChainHeight => todo!(), + R::GeneratedCoins => todo!(), + R::Outputs(map) => todo!(), + R::NumberOutputsWithAmount(vec) => todo!(), + R::CheckKIsNotSpent(set) => todo!(), + R::BlockBatchInRange(range) => todo!(), + } /* TODO: post-request handling, run some code for each request? */ } diff --git a/database/src/service/tests.rs b/database/src/service/tests.rs index 8f80bfc64..03c03dbc2 100644 --- a/database/src/service/tests.rs +++ b/database/src/service/tests.rs @@ -36,6 +36,10 @@ fn init_drop() { let (reader, writer, _tempdir) = init_service(); } +// TODO: +// un-comment and fix these tests when all `{read,write}` +// service functions are implemented. + // /// Send a read request, and receive a response, // /// asserting the response the expected value. // #[tokio::test] diff --git a/database/src/service/write.rs b/database/src/service/write.rs index fbb67b271..b933224cf 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -138,9 +138,11 @@ impl DatabaseWriter { }; // Map [`Request`]'s to specific database functions. - // match request { - // } - todo!() + // TODO: will there be more than 1 write request? + // this won't have to be an enum. + match request { + WriteRequest::WriteBlock(block) => todo!(), + } } } From 98362543c838d5d57ba9bfa6f775e7564008ae15 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 20:38:55 -0400 Subject: [PATCH 05/25] service: create `ReadRequest` function mappings --- database/src/service/read.rs | 91 +++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index 68cf46491..a096c17b2 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -2,8 +2,10 @@ //---------------------------------------------------------------------------------------------------- Import use std::{ + collections::{HashMap, HashSet}, sync::Arc, task::{Context, Poll}, + ops::Range, }; use crossbeam::channel::Receiver; @@ -208,17 +210,20 @@ fn map_request( /* TODO: pre-request handling, run some code for each request? */ use ReadRequest as R; - match request { - R::BlockExtendedHeader(block) => todo!(), - R::BlockHash(block) => todo!(), - R::BlockExtendedHeaderInRange(range) => todo!(), - R::ChainHeight => todo!(), - R::GeneratedCoins => todo!(), - R::Outputs(map) => todo!(), - R::NumberOutputsWithAmount(vec) => todo!(), - R::CheckKIsNotSpent(set) => todo!(), - R::BlockBatchInRange(range) => todo!(), - } + let response = match request { + R::BlockExtendedHeader(block) => block_extended_header(&env, block), + R::BlockHash(block) => block_hash(&env, block), + R::BlockExtendedHeaderInRange(range) => block_extended_header_in_range(&env, range), + R::ChainHeight => chain_height(&env), + R::GeneratedCoins => generated_coins(&env), + R::Outputs(map) => outputs(&env, map), + R::NumberOutputsWithAmount(vec) => number_outputs_with_amount(&env, vec), + R::CheckKIsNotSpent(set) => check_k_is_not_spent(&env, set), + R::BlockBatchInRange(range) => block_batch_in_range(&env, range), + }; + + // TODO: what do we do if this errors? + response_sender.send(response).unwrap(); /* TODO: post-request handling, run some code for each request? */ } @@ -226,6 +231,12 @@ fn map_request( //---------------------------------------------------------------------------------------------------- Handler functions // These are the actual functions that do stuff according to the incoming [`Request`]. // +// Each function name is a 1-1 mapping (from CamelCase -> snake_case) to +// the enum variant name, e.g: `BlockExtendedHeader` -> `block_extended_header`. +// +// Each function will return the [`Response`] that we +// should send back to the caller in [`map_request()`]. +// // INVARIANT: // These functions are called above in `tower::Service::call()` // using a custom threadpool which means any call to `par_*()` functions @@ -233,3 +244,61 @@ fn map_request( // // All functions below assume that this is the case, such that // `par_*()` functions will not block the _global_ rayon thread-pool. + +/// [`ReadRequest::BlockExtendedHeader`]. +#[inline] +fn block_extended_header(env: &Arc, block: u64) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::BlockHash`]. +#[inline] +fn block_hash(env: &Arc, block: u64) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::BlockExtendedHeaderInRange`]. +#[inline] +fn block_extended_header_in_range( + env: &Arc, + range: std::ops::Range, +) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::ChainHeight`]. +#[inline] +fn chain_height(env: &Arc) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::GeneratedCoins`]. +#[inline] +fn generated_coins(env: &Arc) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::Outputs`]. +#[inline] +fn outputs(env: &Arc, map: HashMap>) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::NumberOutputsWithAmount`]. +/// TODO +#[inline] +fn number_outputs_with_amount(env: &Arc, vec: Vec) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::CheckKIsNotSpent`]. +#[inline] +fn check_k_is_not_spent(env: &Arc, set: HashSet<[u8; 32]>) -> ResponseResult { + todo!() +} + +/// [`ReadRequest::BlockBatchInRange`]. +#[inline] +fn block_batch_in_range(env: &Arc, range: Range) -> ResponseResult { + todo!() +} From f81d0ce10775a0ebca65e288c41b08d5e44e7525 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 20:45:49 -0400 Subject: [PATCH 06/25] service: create `WriteRequest` function mappings --- database/src/service/read.rs | 5 ++++- database/src/service/write.rs | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index a096c17b2..a17f81c67 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -3,9 +3,9 @@ //---------------------------------------------------------------------------------------------------- Import use std::{ collections::{HashMap, HashSet}, + ops::Range, sync::Arc, task::{Context, Poll}, - ops::Range, }; use crossbeam::channel::Receiver; @@ -280,6 +280,7 @@ fn generated_coins(env: &Arc) -> ResponseResult { /// [`ReadRequest::Outputs`]. #[inline] +#[allow(clippy::needless_pass_by_value)] // TODO: remove me fn outputs(env: &Arc, map: HashMap>) -> ResponseResult { todo!() } @@ -287,12 +288,14 @@ fn outputs(env: &Arc, map: HashMap>) -> ResponseR /// [`ReadRequest::NumberOutputsWithAmount`]. /// TODO #[inline] +#[allow(clippy::needless_pass_by_value)] // TODO: remove me fn number_outputs_with_amount(env: &Arc, vec: Vec) -> ResponseResult { todo!() } /// [`ReadRequest::CheckKIsNotSpent`]. #[inline] +#[allow(clippy::needless_pass_by_value)] // TODO: remove me fn check_k_is_not_spent(env: &Arc, set: HashSet<[u8; 32]>) -> ResponseResult { todo!() } diff --git a/database/src/service/write.rs b/database/src/service/write.rs index b933224cf..1b158d36c 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -9,7 +9,10 @@ use std::{ use futures::channel::oneshot; use cuprate_helper::asynch::InfallibleOneshotReceiver; -use cuprate_types::service::{Response, WriteRequest}; +use cuprate_types::{ + service::{Response, WriteRequest}, + VerifiedBlockInformation, +}; use crate::{error::RuntimeError, ConcreteEnv, Env}; @@ -54,7 +57,7 @@ impl DatabaseWriteHandle { /// Initialize the single `DatabaseWriter` thread. #[cold] #[inline(never)] // Only called once. - pub(super) fn init(db: Arc) -> Self { + pub(super) fn init(env: Arc) -> Self { // Initialize `Request/Response` channels. let (sender, receiver) = crossbeam::channel::unbounded(); @@ -62,7 +65,7 @@ impl DatabaseWriteHandle { std::thread::Builder::new() .name(WRITER_THREAD_NAME.into()) .spawn(move || { - let this = DatabaseWriter { receiver, db }; + let this = DatabaseWriter { receiver, env }; DatabaseWriter::main(this); }) .unwrap(); @@ -104,7 +107,7 @@ pub(super) struct DatabaseWriter { receiver: crossbeam::channel::Receiver<(WriteRequest, ResponseSender)>, /// Access to the database. - db: Arc, + env: Arc, } impl Drop for DatabaseWriter { @@ -141,7 +144,7 @@ impl DatabaseWriter { // TODO: will there be more than 1 write request? // this won't have to be an enum. match request { - WriteRequest::WriteBlock(block) => todo!(), + WriteRequest::WriteBlock(block) => write_block(&self.env, block), } } } @@ -162,7 +165,7 @@ impl DatabaseWriter { // // We need mutual exclusion due to: // - self.db.resize_map(None); + self.env.resize_map(None); // TODO: // We could pass in custom resizes to account for // batch transactions, i.e., we're about to add ~5GB @@ -170,3 +173,11 @@ impl DatabaseWriter { // } } + +//---------------------------------------------------------------------------------------------------- Handler functions +/// [`WriteRequest::WriteBlock`]. +#[inline] +#[allow(clippy::needless_pass_by_value)] // TODO: remove me +fn write_block(env: &ConcreteEnv, block: VerifiedBlockInformation) { + todo!() +} From cc3b80db0ffe80a5516824c63afa61418ee360e4 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 31 Mar 2024 21:02:16 -0400 Subject: [PATCH 07/25] service: add rough `WriteRequest` retry loop --- database/src/service/write.rs | 42 +++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 1b158d36c..e41ced833 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -18,7 +18,7 @@ use crate::{error::RuntimeError, ConcreteEnv, Env}; //---------------------------------------------------------------------------------------------------- Constants /// Name of the writer thread. -const WRITER_THREAD_NAME: &str = "cuprate_helper::service::read::DatabaseWriter"; +const WRITER_THREAD_NAME: &str = concat!(module_path!(), "::DatabaseWriter"); //---------------------------------------------------------------------------------------------------- Types /// The actual type of the response. @@ -123,6 +123,11 @@ impl DatabaseWriter { #[cold] #[inline(never)] // Only called once. fn main(self) { + /// TODO + const REQUEST_RETRY_LIMIT: usize = 5; + + let mut request_retry_counter = 0; + // 1. Hang on request channel // 2. Map request to some database function // 3. Execute that function, get the result @@ -143,8 +148,37 @@ impl DatabaseWriter { // Map [`Request`]'s to specific database functions. // TODO: will there be more than 1 write request? // this won't have to be an enum. - match request { - WriteRequest::WriteBlock(block) => write_block(&self.env, block), + loop { + // TODO: Instead of reacting to resize errors, we should + // be proactively resizing each request. Else, we must + // `.clone()` the below block as the next `loop` retry + // must need access to it as well. + // + // If we had a local stack `usize` holding our + // current memory map bytes, we could calculate how + // much space we have, and if we need to proactively + // resize before writing some data to avoid a `.clone()`. + + let response = match request { + WriteRequest::WriteBlock(block) => write_block(&self.env, block), + }; + + match response { + // TODO: what do we do if this errors? + Ok(response) => response_sender.send(Ok(response)).unwrap(), + Err(RuntimeError::ResizeNeeded) => { + if request_retry_counter > REQUEST_RETRY_LIMIT { + break; + } + + request_retry_counter += 1; + self.resize_map(); + continue; + } + Err(error) => response_sender.send(Err(error)).unwrap(), + } + + break; } } } @@ -178,6 +212,6 @@ impl DatabaseWriter { /// [`WriteRequest::WriteBlock`]. #[inline] #[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn write_block(env: &ConcreteEnv, block: VerifiedBlockInformation) { +fn write_block(env: &ConcreteEnv, block: VerifiedBlockInformation) -> ResponseResult { todo!() } From 4c5c74797cfe0267448f07da96fd081ae0bb904a Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 1 Apr 2024 15:42:15 -0400 Subject: [PATCH 08/25] service: handle `RuntimeError::ResizeNeeded` in writer --- database/src/service/write.rs | 78 +++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index e41ced833..daddf212d 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -123,11 +123,6 @@ impl DatabaseWriter { #[cold] #[inline(never)] // Only called once. fn main(self) { - /// TODO - const REQUEST_RETRY_LIMIT: usize = 5; - - let mut request_retry_counter = 0; - // 1. Hang on request channel // 2. Map request to some database function // 3. Execute that function, get the result @@ -145,39 +140,60 @@ impl DatabaseWriter { return; }; + /// TODO + #[allow(clippy::items_after_statements)] + const REQUEST_RETRY_LIMIT: usize = if ConcreteEnv::MANUAL_RESIZE { 2 } else { 1 }; + // Map [`Request`]'s to specific database functions. + // // TODO: will there be more than 1 write request? // this won't have to be an enum. - loop { - // TODO: Instead of reacting to resize errors, we should - // be proactively resizing each request. Else, we must - // `.clone()` the below block as the next `loop` retry - // must need access to it as well. - // - // If we had a local stack `usize` holding our - // current memory map bytes, we could calculate how - // much space we have, and if we need to proactively - // resize before writing some data to avoid a `.clone()`. - - let response = match request { + // + // This loop will be: + // - 2 iterations for manually resizing databases + // - 1 iteration for auto resizing databases + // + // Both will: + // 1. Map the request to a function + // 2. Call the function + // 3. (manual resize only) If resize is needed, resize and `continue` + // 4. (manual resize only) Redo step {1, 2} + // 5. Send the function's `Result` back to the requester + for retry in 0..REQUEST_RETRY_LIMIT { + let response = match &request { WriteRequest::WriteBlock(block) => write_block(&self.env, block), }; - match response { - // TODO: what do we do if this errors? - Ok(response) => response_sender.send(Ok(response)).unwrap(), - Err(RuntimeError::ResizeNeeded) => { - if request_retry_counter > REQUEST_RETRY_LIMIT { - break; - } - - request_retry_counter += 1; - self.resize_map(); - continue; - } - Err(error) => response_sender.send(Err(error)).unwrap(), + // If the database needs to resize, do so. + // This branch will only be taken if: + // - This database manually resizes (compile time `bool`) + // - we haven't surpassed the retry limit, [`REQUEST_RETRY_LIMIT`] + if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) + { + // If this is the 2nd iteration of the outer `for` loop and we + // encounter a resize error _again_, it means something is wrong + // as we should have successfully resized last iteration. + assert_eq!( + retry, 0, + "database needed to resize twice - we should have handled it" + ); + + // Resize the map, and retry the request handling loop. + self.resize_map(); + continue; + } + + // Automatically resizing databases should not be returning a resize error. + #[cfg(debug_assertions)] + if !ConcreteEnv::MANUAL_RESIZE { + assert!( + !matches!(response, Err(RuntimeError::ResizeNeeded)), + "auto-resizing database returned a ResizeNeeded error" + ); } + // Send the response back, whether if it's an `Ok` or `Err`. + response_sender.send(response).unwrap(); break; } } @@ -212,6 +228,6 @@ impl DatabaseWriter { /// [`WriteRequest::WriteBlock`]. #[inline] #[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn write_block(env: &ConcreteEnv, block: VerifiedBlockInformation) -> ResponseResult { +fn write_block(env: &ConcreteEnv, block: &VerifiedBlockInformation) -> ResponseResult { todo!() } From da25eeba753ccf755f1ce024de8462aa82210b2e Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 1 Apr 2024 15:44:21 -0400 Subject: [PATCH 09/25] add `{R,r}o` exception to typos --- typos.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/typos.toml b/typos.toml index 299b8eb8a..c31e6f55f 100644 --- a/typos.toml +++ b/typos.toml @@ -6,6 +6,9 @@ extend-ignore-identifiers-re = [ # in file: `/cryptonight/c/oaes_lib.c:1213` # not sure if false-positive or not. "InvMixColums", + # cuprate_database's `TxRo` and `tx_ro` + "Ro", + "ro", ] [files] From 396dde527350051d10db717ce4303138936c526e Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 1 Apr 2024 15:49:24 -0400 Subject: [PATCH 10/25] docs --- database/src/service/read.rs | 7 +++---- database/src/service/write.rs | 13 ++++++++++++- typos.toml | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index a17f81c67..e640324bb 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -197,14 +197,13 @@ impl tower::Service for DatabaseReadHandle { /// /// This is the main entrance into all `Request` handler functions. /// The basic structure is: -/// /// 1. `Request` is mapped to a handler function /// 2. Handler function is called /// 3. [`Response`] is sent fn map_request( - _permit: OwnedSemaphorePermit, // Permit for this request - env: Arc, // Access to the database - request: ReadRequest, // The request we must fulfill + _permit: OwnedSemaphorePermit, // Permit for this request, dropped at end of function + env: Arc, // Access to the database + request: ReadRequest, // The request we must fulfill response_sender: ResponseSender, // The channel we must send the response back to ) { /* TODO: pre-request handling, run some code for each request? */ diff --git a/database/src/service/write.rs b/database/src/service/write.rs index daddf212d..fceef53b8 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -140,7 +140,10 @@ impl DatabaseWriter { return; }; - /// TODO + /// How many times should we retry handling the request on resize errors? + /// + /// This is 1 on automatically resizing databases, meaning there is only 1 iteration. + /// This is 2 on manually resizing databases, meaning we only retry once, as that should be all that's needed. #[allow(clippy::items_after_statements)] const REQUEST_RETRY_LIMIT: usize = if ConcreteEnv::MANUAL_RESIZE { 2 } else { 1 }; @@ -225,6 +228,14 @@ impl DatabaseWriter { } //---------------------------------------------------------------------------------------------------- Handler functions +// These are the actual functions that do stuff according to the incoming [`Request`]. +// +// Each function name is a 1-1 mapping (from CamelCase -> snake_case) to +// the enum variant name, e.g: `BlockExtendedHeader` -> `block_extended_header`. +// +// Each function will return the [`Response`] that we +// should send back to the caller in [`map_request()`]. + /// [`WriteRequest::WriteBlock`]. #[inline] #[allow(clippy::needless_pass_by_value)] // TODO: remove me diff --git a/typos.toml b/typos.toml index c31e6f55f..abab1903b 100644 --- a/typos.toml +++ b/typos.toml @@ -7,6 +7,7 @@ extend-ignore-identifiers-re = [ # not sure if false-positive or not. "InvMixColums", # cuprate_database's `TxRo` and `tx_ro` + "RO", "Ro", "ro", ] From ad2ec3fa46f1c81e3e2af6d14bfd3fde05a5bfe4 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 11 Apr 2024 20:39:42 -0400 Subject: [PATCH 11/25] env: make `resize_map()` return new memory map byte size --- database/src/backend/heed/env.rs | 12 +++++++++--- database/src/env.rs | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/database/src/backend/heed/env.rs b/database/src/backend/heed/env.rs index d9e3fdc21..4bfcc1be4 100644 --- a/database/src/backend/heed/env.rs +++ b/database/src/backend/heed/env.rs @@ -249,11 +249,11 @@ impl Env for ConcreteEnv { Ok(self.env.read().unwrap().force_sync()?) } - fn resize_map(&self, resize_algorithm: Option) { + fn resize_map(&self, resize_algorithm: Option) -> NonZeroUsize { let resize_algorithm = resize_algorithm.unwrap_or_else(|| self.config().resize_algorithm); let current_size_bytes = self.current_map_size(); - let new_size_bytes = resize_algorithm.resize(current_size_bytes).get(); + let new_size_bytes = resize_algorithm.resize(current_size_bytes); // SAFETY: // Resizing requires that we have @@ -264,8 +264,14 @@ impl Env for ConcreteEnv { // unsafe { // INVARIANT: `resize()` returns a valid `usize` to resize to. - self.env.write().unwrap().resize(new_size_bytes).unwrap(); + self.env + .write() + .unwrap() + .resize(new_size_bytes.get()) + .unwrap(); } + + new_size_bytes } #[inline] diff --git a/database/src/env.rs b/database/src/env.rs index 26adc9755..a9333842b 100644 --- a/database/src/env.rs +++ b/database/src/env.rs @@ -1,7 +1,7 @@ //! Abstracted database environment; `trait Env`. //---------------------------------------------------------------------------------------------------- Import -use std::{fmt::Debug, ops::Deref}; +use std::{fmt::Debug, num::NonZeroUsize, ops::Deref}; use crate::{ config::Config, @@ -113,7 +113,7 @@ pub trait Env: Sized { /// This function _must_ be re-implemented if [`Env::MANUAL_RESIZE`] is `true`. /// /// Otherwise, this function will panic with `unreachable!()`. - fn resize_map(&self, resize_algorithm: Option) { + fn resize_map(&self, resize_algorithm: Option) -> NonZeroUsize { unreachable!() } From 71377f5c2c6059f51381370d4a72c1f6aba06873 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 11 Apr 2024 20:40:15 -0400 Subject: [PATCH 12/25] write: proactively handle resizes `add_block()` takes `VerifiedBlockInformation` such that it can just take the inner blobs of data. This is a problem when reactively resizing since we no longer have the block struct we just gave away so we're forced to `.clone()` each retry. Instead of that - we will proactively resize so the resize error will never occur in the first place. --- database/src/service/write.rs | 102 +++++++++++++++++----------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index fceef53b8..69172e413 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -65,7 +65,11 @@ impl DatabaseWriteHandle { std::thread::Builder::new() .name(WRITER_THREAD_NAME.into()) .spawn(move || { - let this = DatabaseWriter { receiver, env }; + let this = DatabaseWriter { + receiver, + env, + env_memory_map_bytes_left: 0, + }; DatabaseWriter::main(this); }) .unwrap(); @@ -108,6 +112,11 @@ pub(super) struct DatabaseWriter { /// Access to the database. env: Arc, + + /// How many free bytes are left in our memory map? + /// + /// This is only used if `ConcreteEnv::MANUAL_RESIZE == true`. + env_memory_map_bytes_left: usize, } impl Drop for DatabaseWriter { @@ -122,7 +131,8 @@ impl DatabaseWriter { /// The writer just loops in this function. #[cold] #[inline(never)] // Only called once. - fn main(self) { + #[allow(unused_mut)] // `self` needs to be mutable for manually resizing DBs + fn main(mut self) { // 1. Hang on request channel // 2. Map request to some database function // 3. Execute that function, get the result @@ -140,65 +150,57 @@ impl DatabaseWriter { return; }; - /// How many times should we retry handling the request on resize errors? - /// - /// This is 1 on automatically resizing databases, meaning there is only 1 iteration. - /// This is 2 on manually resizing databases, meaning we only retry once, as that should be all that's needed. - #[allow(clippy::items_after_statements)] - const REQUEST_RETRY_LIMIT: usize = if ConcreteEnv::MANUAL_RESIZE { 2 } else { 1 }; + // Calculate if we need to resize the memory map. + if ConcreteEnv::MANUAL_RESIZE { + // TODO: calculate the exact byte size needed to store this block. + // I think a adding a slight buffer is ok since we're only taking + // up the memory map, not actual disk space. + // + // We just need to make sure LMDB never returns a resize error. + let block_byte_size_including_heap_memory: usize = todo!(); + + // If our memory map doesn't have enough bytes to store the block, resize. + if block_byte_size_including_heap_memory > self.env_memory_map_bytes_left { + let map_byte_size_before = self.env.current_map_size(); + let map_byte_size_after = self.env.resize_map(None); + + // Add on the bytes we adjusted to. + self.env_memory_map_bytes_left += + map_byte_size_after.get() - map_byte_size_before; + } + } // Map [`Request`]'s to specific database functions. // // TODO: will there be more than 1 write request? // this won't have to be an enum. // - // This loop will be: - // - 2 iterations for manually resizing databases - // - 1 iteration for auto resizing databases - // - // Both will: + // 0. (manual resize only) If resize is needed, resize // 1. Map the request to a function // 2. Call the function - // 3. (manual resize only) If resize is needed, resize and `continue` - // 4. (manual resize only) Redo step {1, 2} - // 5. Send the function's `Result` back to the requester - for retry in 0..REQUEST_RETRY_LIMIT { - let response = match &request { - WriteRequest::WriteBlock(block) => write_block(&self.env, block), - }; - - // If the database needs to resize, do so. - // This branch will only be taken if: - // - This database manually resizes (compile time `bool`) - // - we haven't surpassed the retry limit, [`REQUEST_RETRY_LIMIT`] - if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) - { - // If this is the 2nd iteration of the outer `for` loop and we - // encounter a resize error _again_, it means something is wrong - // as we should have successfully resized last iteration. - assert_eq!( - retry, 0, - "database needed to resize twice - we should have handled it" - ); - - // Resize the map, and retry the request handling loop. - self.resize_map(); - continue; - } + // 3. Send the function's `Result` back to the requester + let response = match &request { + WriteRequest::WriteBlock(block) => write_block(&self.env, block), + }; - // Automatically resizing databases should not be returning a resize error. - #[cfg(debug_assertions)] - if !ConcreteEnv::MANUAL_RESIZE { - assert!( - !matches!(response, Err(RuntimeError::ResizeNeeded)), - "auto-resizing database returned a ResizeNeeded error" - ); - } + // INVARIANT: We proactively resized above, this error should never be returned. + #[allow(clippy::manual_assert)] + #[cfg(debug_assertions)] + if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) { + panic!("the database was proactively resized, yet a resize error was returned"); + } - // Send the response back, whether if it's an `Ok` or `Err`. - response_sender.send(response).unwrap(); - break; + // Automatically resizing databases should not be returning a resize error. + #[cfg(debug_assertions)] + if !ConcreteEnv::MANUAL_RESIZE { + assert!( + !matches!(response, Err(RuntimeError::ResizeNeeded)), + "auto-resizing database returned a ResizeNeeded error" + ); } + + // Send the response back, whether if it's an `Ok` or `Err`. + response_sender.send(response).unwrap(); } } From 684ad0d6cd68a5b32bb28d5086b6a42cc22b6513 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 11 Apr 2024 20:42:00 -0400 Subject: [PATCH 13/25] read: use type aliases --- database/src/service/read.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index e640324bb..ed4228875 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -18,7 +18,12 @@ use tokio_util::sync::PollSemaphore; use cuprate_helper::asynch::InfallibleOneshotReceiver; use cuprate_types::service::{ReadRequest, Response}; -use crate::{config::ReaderThreads, error::RuntimeError, ConcreteEnv}; +use crate::{ + config::ReaderThreads, + error::RuntimeError, + types::{Amount, AmountIndex, BlockHeight, KeyImage}, + ConcreteEnv, +}; //---------------------------------------------------------------------------------------------------- Types /// The actual type of the response. @@ -153,10 +158,8 @@ impl tower::Service for DatabaseReadHandle { } // Acquire a permit before returning `Ready`. - let Some(permit) = ready!(self.semaphore.poll_acquire(cx)) else { - // `self` itself owns the backing semaphore, so it can't be closed. - unreachable!(); - }; + let permit = ready!(self.semaphore.poll_acquire(cx)) + .expect("`self` itself owns the backing semaphore, so it can't be closed."); self.permit = Some(permit); Poll::Ready(Ok(())) @@ -246,13 +249,13 @@ fn map_request( /// [`ReadRequest::BlockExtendedHeader`]. #[inline] -fn block_extended_header(env: &Arc, block: u64) -> ResponseResult { +fn block_extended_header(env: &Arc, block_height: BlockHeight) -> ResponseResult { todo!() } /// [`ReadRequest::BlockHash`]. #[inline] -fn block_hash(env: &Arc, block: u64) -> ResponseResult { +fn block_hash(env: &Arc, block_height: BlockHeight) -> ResponseResult { todo!() } @@ -260,7 +263,7 @@ fn block_hash(env: &Arc, block: u64) -> ResponseResult { #[inline] fn block_extended_header_in_range( env: &Arc, - range: std::ops::Range, + range: std::ops::Range, ) -> ResponseResult { todo!() } @@ -280,7 +283,7 @@ fn generated_coins(env: &Arc) -> ResponseResult { /// [`ReadRequest::Outputs`]. #[inline] #[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn outputs(env: &Arc, map: HashMap>) -> ResponseResult { +fn outputs(env: &Arc, map: HashMap>) -> ResponseResult { todo!() } @@ -288,19 +291,19 @@ fn outputs(env: &Arc, map: HashMap>) -> ResponseR /// TODO #[inline] #[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn number_outputs_with_amount(env: &Arc, vec: Vec) -> ResponseResult { +fn number_outputs_with_amount(env: &Arc, vec: Vec) -> ResponseResult { todo!() } /// [`ReadRequest::CheckKIsNotSpent`]. #[inline] #[allow(clippy::needless_pass_by_value)] // TODO: remove me -fn check_k_is_not_spent(env: &Arc, set: HashSet<[u8; 32]>) -> ResponseResult { +fn check_k_is_not_spent(env: &Arc, set: HashSet) -> ResponseResult { todo!() } /// [`ReadRequest::BlockBatchInRange`]. #[inline] -fn block_batch_in_range(env: &Arc, range: Range) -> ResponseResult { +fn block_batch_in_range(env: &Arc, range: Range) -> ResponseResult { todo!() } From c7961ec08ea07c3a9dbe088bbec77e2989c695c7 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 11 Apr 2024 20:48:24 -0400 Subject: [PATCH 14/25] docs --- database/src/service/write.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 69172e413..e8cf5b68a 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -134,6 +134,7 @@ impl DatabaseWriter { #[allow(unused_mut)] // `self` needs to be mutable for manually resizing DBs fn main(mut self) { // 1. Hang on request channel + // 1b. (manual resize only) If resize is needed, resize // 2. Map request to some database function // 3. Execute that function, get the result // 4. Return the result via channel @@ -174,11 +175,6 @@ impl DatabaseWriter { // // TODO: will there be more than 1 write request? // this won't have to be an enum. - // - // 0. (manual resize only) If resize is needed, resize - // 1. Map the request to a function - // 2. Call the function - // 3. Send the function's `Result` back to the requester let response = match &request { WriteRequest::WriteBlock(block) => write_block(&self.env, block), }; From 627564446e61d1c105a6d78d79e9f604ad8d8573 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 11 Apr 2024 21:08:04 -0400 Subject: [PATCH 15/25] fix import --- database/src/backend/heed/env.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/database/src/backend/heed/env.rs b/database/src/backend/heed/env.rs index 4bfcc1be4..9362cd721 100644 --- a/database/src/backend/heed/env.rs +++ b/database/src/backend/heed/env.rs @@ -4,6 +4,7 @@ use std::{ cell::RefCell, fmt::Debug, + num::NonZeroUsize, ops::Deref, sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, }; From d8cd2fc5b880038c5dac0f4fafde7306e91e7f05 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 14:37:48 -0400 Subject: [PATCH 16/25] write: handle resizes reactively --- database/src/service/write.rs | 142 +++++++++++++++------------------- 1 file changed, 64 insertions(+), 78 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index e8cf5b68a..98131f4ca 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -65,11 +65,7 @@ impl DatabaseWriteHandle { std::thread::Builder::new() .name(WRITER_THREAD_NAME.into()) .spawn(move || { - let this = DatabaseWriter { - receiver, - env, - env_memory_map_bytes_left: 0, - }; + let this = DatabaseWriter { receiver, env }; DatabaseWriter::main(this); }) .unwrap(); @@ -112,11 +108,6 @@ pub(super) struct DatabaseWriter { /// Access to the database. env: Arc, - - /// How many free bytes are left in our memory map? - /// - /// This is only used if `ConcreteEnv::MANUAL_RESIZE == true`. - env_memory_map_bytes_left: usize, } impl Drop for DatabaseWriter { @@ -131,10 +122,8 @@ impl DatabaseWriter { /// The writer just loops in this function. #[cold] #[inline(never)] // Only called once. - #[allow(unused_mut)] // `self` needs to be mutable for manually resizing DBs - fn main(mut self) { + fn main(self) { // 1. Hang on request channel - // 1b. (manual resize only) If resize is needed, resize // 2. Map request to some database function // 3. Execute that function, get the result // 4. Return the result via channel @@ -151,77 +140,75 @@ impl DatabaseWriter { return; }; - // Calculate if we need to resize the memory map. - if ConcreteEnv::MANUAL_RESIZE { - // TODO: calculate the exact byte size needed to store this block. - // I think a adding a slight buffer is ok since we're only taking - // up the memory map, not actual disk space. - // - // We just need to make sure LMDB never returns a resize error. - let block_byte_size_including_heap_memory: usize = todo!(); - - // If our memory map doesn't have enough bytes to store the block, resize. - if block_byte_size_including_heap_memory > self.env_memory_map_bytes_left { - let map_byte_size_before = self.env.current_map_size(); - let map_byte_size_after = self.env.resize_map(None); - - // Add on the bytes we adjusted to. - self.env_memory_map_bytes_left += - map_byte_size_after.get() - map_byte_size_before; - } - } + /// How many times should we retry handling the request on resize errors? + /// + /// This is 1 on automatically resizing databases, meaning there is only 1 iteration. + #[allow(clippy::items_after_statements)] + const REQUEST_RETRY_LIMIT: usize = if ConcreteEnv::MANUAL_RESIZE { 3 } else { 1 }; // Map [`Request`]'s to specific database functions. // - // TODO: will there be more than 1 write request? - // this won't have to be an enum. - let response = match &request { - WriteRequest::WriteBlock(block) => write_block(&self.env, block), - }; - - // INVARIANT: We proactively resized above, this error should never be returned. - #[allow(clippy::manual_assert)] - #[cfg(debug_assertions)] - if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) { - panic!("the database was proactively resized, yet a resize error was returned"); - } + // This loop will be: + // - 2 iterations for manually resizing databases + // - 1 iteration for auto resizing databases + // + // Both will: + // 1. Map the request to a function + // 2. Call the function + // 3. (manual resize only) If resize is needed, resize and `continue` + // 4. (manual resize only) Redo step {1, 2} + // 5. Send the function's `Result` back to the requester + for retry in 0..REQUEST_RETRY_LIMIT { + // TODO: will there be more than 1 write request? + // this won't have to be an enum. + let response = match &request { + WriteRequest::WriteBlock(block) => write_block(&self.env, block), + }; - // Automatically resizing databases should not be returning a resize error. - #[cfg(debug_assertions)] - if !ConcreteEnv::MANUAL_RESIZE { - assert!( - !matches!(response, Err(RuntimeError::ResizeNeeded)), - "auto-resizing database returned a ResizeNeeded error" - ); - } + // If the database needs to resize, do so. This branch will only be taken if: + // - This database manually resizes (compile time `bool`) + // - we haven't surpassed the retry limit, [`REQUEST_RETRY_LIMIT`] + if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) + { + // If this is the 2nd iteration of the outer `for` loop and we + // encounter a resize error _again_, it means something is wrong + // as we should have successfully resized last iteration. + assert!( + retry < REQUEST_RETRY_LIMIT, + "database resize failed {REQUEST_RETRY_LIMIT} times" + ); + + // Resize the map, and retry the request handling loop. + // + // FIXME: + // We could pass in custom resizes to account for + // batches, i.e., we're about to add ~5GB of data, + // add that much instead of the default 1GB. + // + let old = self.env.current_map_size(); + let new = self.env.resize_map(None); + + // TODO: use tracing. + println!("resizing database memory map, old: {old}B, new: {new}B"); + + // Try handling the request again. + continue; + } - // Send the response back, whether if it's an `Ok` or `Err`. - response_sender.send(response).unwrap(); - } - } + // Automatically resizing databases should not be returning a resize error. + #[cfg(debug_assertions)] + if !ConcreteEnv::MANUAL_RESIZE { + assert!( + !matches!(response, Err(RuntimeError::ResizeNeeded)), + "auto-resizing database returned a ResizeNeeded error" + ); + } - /// Resize the database's memory map. - fn resize_map(&self) { - // The compiler most likely optimizes out this - // entire function call if this returns here. - if !ConcreteEnv::MANUAL_RESIZE { - return; + // Send the response back, whether if it's an `Ok` or `Err`. + response_sender.send(response).unwrap(); + break; + } } - - // INVARIANT: - // [`Env`]'s that are `MANUAL_RESIZE` are expected to implement - // their internals such that we have exclusive access when calling - // this function. We do not handle the exclusion part, `resize_map()` - // itself does. The `heed` backend does this with `RwLock`. - // - // We need mutual exclusion due to: - // - self.env.resize_map(None); - // TODO: - // We could pass in custom resizes to account for - // batch transactions, i.e., we're about to add ~5GB - // of data, add that much instead of the default 1GB. - // } } @@ -236,7 +223,6 @@ impl DatabaseWriter { /// [`WriteRequest::WriteBlock`]. #[inline] -#[allow(clippy::needless_pass_by_value)] // TODO: remove me fn write_block(env: &ConcreteEnv, block: &VerifiedBlockInformation) -> ResponseResult { todo!() } From dbcf35f629870739b257636c6561a6b513852369 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 14:40:43 -0400 Subject: [PATCH 17/25] service: panic if response can't be sent back --- database/src/service/read.rs | 5 +++-- database/src/service/write.rs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index ed4228875..190963cd8 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -224,8 +224,9 @@ fn map_request( R::BlockBatchInRange(range) => block_batch_in_range(&env, range), }; - // TODO: what do we do if this errors? - response_sender.send(response).unwrap(); + response_sender + .send(response) + .expect("database reader thread failed to send response back to requester"); /* TODO: post-request handling, run some code for each request? */ } diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 98131f4ca..6f2e90c2c 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -205,7 +205,9 @@ impl DatabaseWriter { } // Send the response back, whether if it's an `Ok` or `Err`. - response_sender.send(response).unwrap(); + response_sender + .send(response) + .expect("database writer thread failed to send response back to requester"); break; } } From 0fe535d68bb8d8dcd08c196e54ecff911bf537ab Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 15:08:11 -0400 Subject: [PATCH 18/25] write: add loop unreachable asserts --- database/src/service/read.rs | 4 ++-- database/src/service/write.rs | 26 +++++++++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index 190963cd8..4fb8b566f 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -165,6 +165,7 @@ impl tower::Service for DatabaseReadHandle { Poll::Ready(Ok(())) } + #[inline] fn call(&mut self, request: ReadRequest) -> Self::Future { let permit = self .permit @@ -194,8 +195,7 @@ impl tower::Service for DatabaseReadHandle { // This function maps [`Request`]s to function calls // executed by the rayon DB reader threadpool. -#[inline] -#[allow(clippy::needless_pass_by_value)] +#[allow(clippy::needless_pass_by_value)] // TODO: fix me /// Map [`Request`]'s to specific database handler functions. /// /// This is the main entrance into all `Request` handler functions. diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 6f2e90c2c..583c8b4f9 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -127,7 +127,7 @@ impl DatabaseWriter { // 2. Map request to some database function // 3. Execute that function, get the result // 4. Return the result via channel - loop { + 'main: loop { let Ok((request, response_sender)) = self.receiver.recv() else { // If this receive errors, it means that the channel is empty // and disconnected, meaning the other side (all senders) have @@ -158,7 +158,11 @@ impl DatabaseWriter { // 3. (manual resize only) If resize is needed, resize and `continue` // 4. (manual resize only) Redo step {1, 2} // 5. Send the function's `Result` back to the requester - for retry in 0..REQUEST_RETRY_LIMIT { + // + // FIXME: there's probably a more elegant way + // represent this retry logic with recursive + // functions instead of loops + stack state. + 'retry: for retry in 0..=REQUEST_RETRY_LIMIT { // TODO: will there be more than 1 write request? // this won't have to be an enum. let response = match &request { @@ -170,7 +174,7 @@ impl DatabaseWriter { // - we haven't surpassed the retry limit, [`REQUEST_RETRY_LIMIT`] if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) { - // If this is the 2nd iteration of the outer `for` loop and we + // If this is the last iteration of the outer `for` loop and we // encounter a resize error _again_, it means something is wrong // as we should have successfully resized last iteration. assert!( @@ -192,7 +196,7 @@ impl DatabaseWriter { println!("resizing database memory map, old: {old}B, new: {new}B"); // Try handling the request again. - continue; + continue 'retry; } // Automatically resizing databases should not be returning a resize error. @@ -208,9 +212,21 @@ impl DatabaseWriter { response_sender .send(response) .expect("database writer thread failed to send response back to requester"); - break; + + continue 'main; } + + // Above retry loop should either: + // - continue to the next ['main] loop or... + // - ...retry until panic + unreachable!(); } + + // The only case the ['main] loop breaks should be a: + // - direct function return + // - panic + // anything below should be unreachable. + unreachable!(); } } From 640b54eb3cd71383aaddbd92bc0d2df387409b6e Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 15:10:57 -0400 Subject: [PATCH 19/25] service: print and drop error instead of panic --- database/src/service/read.rs | 7 ++++--- database/src/service/write.rs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index 4fb8b566f..cb83e8d81 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -224,9 +224,10 @@ fn map_request( R::BlockBatchInRange(range) => block_batch_in_range(&env, range), }; - response_sender - .send(response) - .expect("database reader thread failed to send response back to requester"); + if let Err(e) = response_sender.send(response) { + // TODO: use tracing. + println!("database reader failed to send response: {e:?}"); + } /* TODO: post-request handling, run some code for each request? */ } diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 583c8b4f9..3dba13539 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -209,9 +209,10 @@ impl DatabaseWriter { } // Send the response back, whether if it's an `Ok` or `Err`. - response_sender - .send(response) - .expect("database writer thread failed to send response back to requester"); + if let Err(e) = response_sender.send(response) { + // TODO: use tracing. + println!("database writer failed to send response: {e:?}"); + } continue 'main; } From 37a4317dd343054ab4ed36b70a95571b70676763 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 15:18:22 -0400 Subject: [PATCH 20/25] write: fix retry loop off-by-1 --- database/src/service/write.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 3dba13539..e1909a196 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -162,7 +162,7 @@ impl DatabaseWriter { // FIXME: there's probably a more elegant way // represent this retry logic with recursive // functions instead of loops + stack state. - 'retry: for retry in 0..=REQUEST_RETRY_LIMIT { + 'retry: for retry in 0..REQUEST_RETRY_LIMIT { // TODO: will there be more than 1 write request? // this won't have to be an enum. let response = match &request { @@ -175,11 +175,10 @@ impl DatabaseWriter { if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) { // If this is the last iteration of the outer `for` loop and we - // encounter a resize error _again_, it means something is wrong - // as we should have successfully resized last iteration. - assert!( - retry < REQUEST_RETRY_LIMIT, - "database resize failed {REQUEST_RETRY_LIMIT} times" + // encounter a resize error _again_, it means something is wrong. + assert_ne!( + retry, REQUEST_RETRY_LIMIT, + "database resize failed maximum of {REQUEST_RETRY_LIMIT} times" ); // Resize the map, and retry the request handling loop. From 9107cb127973cd570734cf61bfb90561d537c38a Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 15:25:54 -0400 Subject: [PATCH 21/25] write: fix docs --- database/src/service/write.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/database/src/service/write.rs b/database/src/service/write.rs index e1909a196..57118c5b8 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -119,7 +119,8 @@ impl Drop for DatabaseWriter { impl DatabaseWriter { /// The `DatabaseWriter`'s main function. /// - /// The writer just loops in this function. + /// The writer just loops in this function, handling requests forever + /// until the request channel is dropped or a panic occurs. #[cold] #[inline(never)] // Only called once. fn main(self) { @@ -148,20 +149,16 @@ impl DatabaseWriter { // Map [`Request`]'s to specific database functions. // - // This loop will be: - // - 2 iterations for manually resizing databases - // - 1 iteration for auto resizing databases - // // Both will: // 1. Map the request to a function // 2. Call the function - // 3. (manual resize only) If resize is needed, resize and `continue` + // 3. (manual resize only) If resize is needed, resize and retry // 4. (manual resize only) Redo step {1, 2} // 5. Send the function's `Result` back to the requester // // FIXME: there's probably a more elegant way - // represent this retry logic with recursive - // functions instead of loops + stack state. + // to represent this retry logic with recursive + // functions instead of a loop. 'retry: for retry in 0..REQUEST_RETRY_LIMIT { // TODO: will there be more than 1 write request? // this won't have to be an enum. @@ -169,9 +166,7 @@ impl DatabaseWriter { WriteRequest::WriteBlock(block) => write_block(&self.env, block), }; - // If the database needs to resize, do so. This branch will only be taken if: - // - This database manually resizes (compile time `bool`) - // - we haven't surpassed the retry limit, [`REQUEST_RETRY_LIMIT`] + // If the database needs to resize, do so. if ConcreteEnv::MANUAL_RESIZE && matches!(response, Err(RuntimeError::ResizeNeeded)) { // If this is the last iteration of the outer `for` loop and we From c9b46bc025747b005143691ccd73a3234ae4bfe4 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 18:03:01 -0400 Subject: [PATCH 22/25] review changes --- database/src/env.rs | 2 ++ database/src/resize.rs | 2 ++ database/src/service/mod.rs | 3 +++ database/src/service/read.rs | 26 +++----------------------- database/src/service/types.rs | 31 +++++++++++++++++++++++++++++++ database/src/service/write.rs | 21 +++++---------------- 6 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 database/src/service/types.rs diff --git a/database/src/env.rs b/database/src/env.rs index a9333842b..4109d8894 100644 --- a/database/src/env.rs +++ b/database/src/env.rs @@ -109,6 +109,8 @@ pub trait Env: Sized { /// /// If `resize_algorithm` is `Some`, that will be used instead. /// + /// This function returns the _new_ memory map size in bytes. + /// /// # Invariant /// This function _must_ be re-implemented if [`Env::MANUAL_RESIZE`] is `true`. /// diff --git a/database/src/resize.rs b/database/src/resize.rs index 62ecf5e79..ac54781cb 100644 --- a/database/src/resize.rs +++ b/database/src/resize.rs @@ -59,6 +59,8 @@ impl ResizeAlgorithm { } /// Maps the `self` variant to the free functions in [`crate::resize`]. + /// + /// This function returns the _new_ memory map size in bytes. #[inline] pub fn resize(&self, current_size_bytes: usize) -> NonZeroUsize { match self { diff --git a/database/src/service/mod.rs b/database/src/service/mod.rs index ab4873fa6..887730528 100644 --- a/database/src/service/mod.rs +++ b/database/src/service/mod.rs @@ -67,5 +67,8 @@ pub use write::DatabaseWriteHandle; mod free; pub use free::init; +// Internal type aliases for `service`. +mod types; + #[cfg(test)] mod tests; diff --git a/database/src/service/read.rs b/database/src/service/read.rs index cb83e8d81..c951c6e55 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -21,31 +21,11 @@ use cuprate_types::service::{ReadRequest, Response}; use crate::{ config::ReaderThreads, error::RuntimeError, + service::types::{ResponseReceiver, ResponseResult, ResponseSender}, types::{Amount, AmountIndex, BlockHeight, KeyImage}, ConcreteEnv, }; -//---------------------------------------------------------------------------------------------------- Types -/// The actual type of the response. -/// -/// Either our [`Response`], or a database error occurred. -type ResponseResult = Result; - -/// The `Receiver` channel that receives the read response. -/// -/// This is owned by the caller (the reader) -/// who `.await`'s for the response. -/// -/// The channel itself should never fail, -/// but the actual database operation might. -type ResponseReceiver = InfallibleOneshotReceiver; - -/// The `Sender` channel for the response. -/// -/// The database reader thread uses this to send -/// the database result to the caller. -type ResponseSender = oneshot::Sender; - //---------------------------------------------------------------------------------------------------- DatabaseReadHandle /// Read handle to the database. /// @@ -158,8 +138,8 @@ impl tower::Service for DatabaseReadHandle { } // Acquire a permit before returning `Ready`. - let permit = ready!(self.semaphore.poll_acquire(cx)) - .expect("`self` itself owns the backing semaphore, so it can't be closed."); + let permit = + ready!(self.semaphore.poll_acquire(cx)).expect("this semaphore is never closed"); self.permit = Some(permit); Poll::Ready(Ok(())) diff --git a/database/src/service/types.rs b/database/src/service/types.rs new file mode 100644 index 000000000..265bf42ca --- /dev/null +++ b/database/src/service/types.rs @@ -0,0 +1,31 @@ +//! Database service type aliases. +//! +//! Only used internally for our `tower::Service` impls. + +//---------------------------------------------------------------------------------------------------- Use +use futures::channel::oneshot::Sender; + +use cuprate_helper::asynch::InfallibleOneshotReceiver; +use cuprate_types::service::Response; + +use crate::error::RuntimeError; + +//---------------------------------------------------------------------------------------------------- Types +/// The actual type of the response. +/// +/// Either our [`Response`], or a database error occurred. +pub(super) type ResponseResult = Result; + +/// The `Receiver` channel that receives the read response. +/// +/// This is owned by the caller (the reader/writer thread) +/// who `.await`'s for the response. +/// +/// The channel itself should never fail, +/// but the actual database operation might. +pub(super) type ResponseReceiver = InfallibleOneshotReceiver; + +/// The `Sender` channel for the response. +/// +/// The database reader/writer thread uses this to send the database result to the caller. +pub(super) type ResponseSender = Sender; diff --git a/database/src/service/write.rs b/database/src/service/write.rs index 57118c5b8..e05f9ad2f 100644 --- a/database/src/service/write.rs +++ b/database/src/service/write.rs @@ -14,27 +14,16 @@ use cuprate_types::{ VerifiedBlockInformation, }; -use crate::{error::RuntimeError, ConcreteEnv, Env}; +use crate::{ + error::RuntimeError, + service::types::{ResponseReceiver, ResponseResult, ResponseSender}, + ConcreteEnv, Env, +}; //---------------------------------------------------------------------------------------------------- Constants /// Name of the writer thread. const WRITER_THREAD_NAME: &str = concat!(module_path!(), "::DatabaseWriter"); -//---------------------------------------------------------------------------------------------------- Types -/// The actual type of the response. -/// -/// Either our [Response], or a database error occurred. -type ResponseResult = Result; - -/// The `Receiver` channel that receives the write response. -/// -/// The channel itself should never fail, -/// but the actual database operation might. -type ResponseReceiver = InfallibleOneshotReceiver; - -/// The `Sender` channel for the response. -type ResponseSender = oneshot::Sender; - //---------------------------------------------------------------------------------------------------- DatabaseWriteHandle /// Write handle to the database. /// From 6106013cbe0516b66066ff2926d979fd2b974bf8 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 18:07:32 -0400 Subject: [PATCH 23/25] update readme --- database/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/database/README.md b/database/README.md index 74d07149b..bcab4973b 100644 --- a/database/README.md +++ b/database/README.md @@ -108,6 +108,7 @@ This folder contains the `cupate_database::service` module. | `request.rs` | Read/write `Request`s to the database | `response.rs` | Read/write `Response`'s from the database | `tests.rs` | Thread-pool tests and test helper functions +| `types.rs` | `cuprate_database::service`-related type aliases | `write.rs` | Write thread-pool definitions and logic ## `src/backend/` From 7f7f1d4283e784c4e73459af575dc9faab04c910 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 14 Apr 2024 19:35:05 -0400 Subject: [PATCH 24/25] remove `BlockBatchInRange` request/response --- database/src/service/read.rs | 7 ------- types/src/service.rs | 4 ---- 2 files changed, 11 deletions(-) diff --git a/database/src/service/read.rs b/database/src/service/read.rs index c951c6e55..cabe33503 100644 --- a/database/src/service/read.rs +++ b/database/src/service/read.rs @@ -201,7 +201,6 @@ fn map_request( R::Outputs(map) => outputs(&env, map), R::NumberOutputsWithAmount(vec) => number_outputs_with_amount(&env, vec), R::CheckKIsNotSpent(set) => check_k_is_not_spent(&env, set), - R::BlockBatchInRange(range) => block_batch_in_range(&env, range), }; if let Err(e) = response_sender.send(response) { @@ -283,9 +282,3 @@ fn number_outputs_with_amount(env: &Arc, vec: Vec) -> Respo fn check_k_is_not_spent(env: &Arc, set: HashSet) -> ResponseResult { todo!() } - -/// [`ReadRequest::BlockBatchInRange`]. -#[inline] -fn block_batch_in_range(env: &Arc, range: Range) -> ResponseResult { - todo!() -} diff --git a/types/src/service.rs b/types/src/service.rs index 97344f648..2917536b4 100644 --- a/types/src/service.rs +++ b/types/src/service.rs @@ -37,8 +37,6 @@ pub enum ReadRequest { NumberOutputsWithAmount(Vec), /// TODO CheckKIsNotSpent(HashSet<[u8; 32]>), - /// TODO - BlockBatchInRange(Range), } //---------------------------------------------------------------------------------------------------- WriteRequest @@ -73,8 +71,6 @@ pub enum Response { /// TODO /// returns true if key images are spent CheckKIsNotSpent(bool), - /// TODO - BlockBatchInRange(Vec<(Block, Vec)>), //------------------------------------------------------ Writes /// TODO From da08b25d07b5e9aede63295dc1513b3ec3224c01 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Tue, 16 Apr 2024 17:40:58 -0400 Subject: [PATCH 25/25] Update database/README.md Co-authored-by: Boog900 --- database/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/database/README.md b/database/README.md index bcab4973b..6d47d2b6a 100644 --- a/database/README.md +++ b/database/README.md @@ -105,8 +105,6 @@ This folder contains the `cupate_database::service` module. |----------------|---------| | `free.rs` | General free functions used (related to `cuprate_database::service`) | `read.rs` | Read thread-pool definitions and logic -| `request.rs` | Read/write `Request`s to the database -| `response.rs` | Read/write `Response`'s from the database | `tests.rs` | Thread-pool tests and test helper functions | `types.rs` | `cuprate_database::service`-related type aliases | `write.rs` | Write thread-pool definitions and logic