cuprate-hinto-janai/storage/blockchain/src/service/mod.rs
Boog900 ecd077b402
cuprated: config & args (#304)
* init config

* split sections

* finish initial config.

* fix clap

* misc changes

* fix doc

* fix test & clippy

* fix test 2

* try fix windows

* testing

* testing 2

* fix windows test

* fix windows: the remix.

* review comments

* fix imports

* rename & fix default config file

* fix cargo hack

* enable serde on `cuprate-helper`

* changes from matrix chats

* fix ci

* fix doc

* fix doc test

* move Cuprated.toml

* remove default.rs

* `size` -> `bytes`

* `addressbook_path` -> `address_book_path`

* fix config output

* fix ci

* Update binaries/cuprated/src/config/args.rs

Co-authored-by: hinto-janai <hinto.janai@protonmail.com>

---------

Co-authored-by: hinto-janai <hinto.janai@protonmail.com>
2024-12-03 15:17:21 +00:00

136 lines
4.7 KiB
Rust

//! [`tower::Service`] integeration + thread-pool.
//!
//! ## `service`
//! The `service` module implements the [`tower`] integration,
//! along with the reader/writer thread-pool system.
//!
//! The thread-pool allows outside crates to communicate with it by
//! 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.
//!
//! ## Handles
//! The 2 handles to the database are:
//! - [`BlockchainReadHandle`]
//! - [`BlockchainWriteHandle`]
//!
//! The 1st allows any caller to send [`ReadRequest`][req_r]s.
//!
//! The 2nd allows any caller to send [`WriteRequest`][req_w]s.
//!
//! The [`BlockchainReadHandle`] can be shared as it is cheaply [`Clone`]able, however,
//! the [`BlockchainWriteHandle`] cannot be cloned. There is only 1 place in Cuprate that
//! writes, so it is passed there and used.
//!
//! ## Initialization
//! The database & thread-pool system can be initialized with [`init()`].
//!
//! This causes the underlying database/threads to be setup
//! and returns a read/write handle to that database.
//!
//! ## Shutdown
//! Upon the above handles being dropped, the corresponding thread(s) will automatically exit, i.e:
//! - The last [`BlockchainReadHandle`] is dropped => reader thread-pool exits
//! - The last [`BlockchainWriteHandle`] is dropped => writer thread exits
//!
//! TODO: update this when `ConcreteEnv` is removed
//!
//! Upon dropping the [`cuprate_database::ConcreteEnv`]:
//! - All un-processed database transactions are completed
//! - All data gets flushed to disk (caused by [`Drop::drop`] impl on `ConcreteEnv`)
//!
//! ## Request and Response
//! To interact with the database (whether reading or writing data),
//! a `Request` can be sent using one of the above handles.
//!
//! Both the handles implement `tower::Service`, so they can be [`tower::Service::call`]ed.
//!
//! 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::blockchain::BlockchainReadRequest
//!
//! [req_w]: cuprate_types::blockchain::BlockchainWriteRequest
//!
//! [resp]: cuprate_types::blockchain::BlockchainResponse
//!
//! # Example
//! Simple usage of `service`.
//!
//! ```rust
//! use hex_literal::hex;
//! use tower::{Service, ServiceExt};
//!
//! use cuprate_types::{blockchain::{BlockchainReadRequest, BlockchainWriteRequest, BlockchainResponse}, Chain};
//! use cuprate_test_utils::data::BLOCK_V16_TX0;
//!
//! use cuprate_blockchain::{
//! cuprate_database::Env,
//! config::ConfigBuilder,
//! };
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Create a configuration for the database environment.
//! let tmp_dir = tempfile::tempdir()?;
//! let db_dir = tmp_dir.path().to_owned();
//! let config = ConfigBuilder::new()
//! .data_directory(db_dir.into())
//! .build();
//!
//! // Initialize the database thread-pool.
//! let (mut read_handle, mut write_handle, _) = cuprate_blockchain::service::init(config)?;
//!
//! // Prepare a request to write block.
//! let mut block = BLOCK_V16_TX0.clone();
//! # block.height = 0_usize; // must be 0th height or panic in `add_block()`
//! let request = BlockchainWriteRequest::WriteBlock(block);
//!
//! // Send the request.
//! // We receive back an `async` channel that will
//! // eventually yield the result when `service`
//! // is done writing the block.
//! let response_channel = write_handle.ready().await?.call(request);
//!
//! // Block write was OK.
//! let response = response_channel.await?;
//! assert_eq!(response, BlockchainResponse::Ok);
//!
//! // Now, let's try getting the block hash
//! // of the block we just wrote.
//! let request = BlockchainReadRequest::BlockHash(0, Chain::Main);
//! let response_channel = read_handle.ready().await?.call(request);
//! let response = response_channel.await?;
//! assert_eq!(
//! response,
//! BlockchainResponse::BlockHash(
//! hex!("43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428")
//! )
//! );
//!
//! // This causes the writer thread on the
//! // other side of this handle to exit...
//! drop(write_handle);
//! // ...and this causes the reader thread-pool to exit.
//! drop(read_handle);
//! # Ok(()) }
//! ```
// needed for docs
use tower as _;
mod read;
pub use read::{init_read_service, init_read_service_with_pool};
mod write;
pub use write::init_write_service;
mod free;
pub use free::init;
mod types;
pub use types::{BlockchainReadHandle, BlockchainWriteHandle};
#[cfg(test)]
mod tests;