block-db 0.2.0

Local, multi-threaded, durable byte DB.
Documentation
// Authors: Robert Lopez

use super::{
    data_file::DataFile, error::Error, util::fs::delete_directory, wal::BlockDBLog, BlockDB,
    BlockKey,
};
use std::sync::Arc;
use tokio::sync::RwLock;

impl BlockDB {
    /// Write bytes into a `DataFile`, creating a new `DataBlock`.
    ///
    /// Returns `BlockKey { data_file_id, data_block_id }`.
    ///
    /// See [Write Distribution](https://gitlab.com/robertlopezdev/block-db/-/blob/main/README.md#write-distribution) in the README for more information.
    ///
    /// ---
    /// - **Atomic**
    /// - **Non-corruptible**
    ///
    /// ---
    /// Example
    /// ``` let block_db = BlockDB::open("./data", None).await?;
    ///
    /// let BlockKey { data_file_id, data_block_id } = block_db.write(b"Hello World").await?;
    /// ```
    pub async fn write<B: AsRef<[u8]>>(&self, bytes: B) -> Result<BlockKey, Error> {
        for (_, data_file) in self.data_files.read().await.clone() {
            let reader = data_file.read().await;

            if !reader.is_full() {
                let data_file_id = reader.id.clone();
                drop(reader);

                return Ok(BlockKey {
                    data_file_id,
                    data_block_id: data_file.write().await.write(bytes.as_ref()).await?,
                });
            }
        }

        let new_data_file_id = DataFile::generate_id();
        let new_data_file_path = self.path.join("data_files").join(&new_data_file_id);

        match DataFile::open(
            new_data_file_id,
            new_data_file_path.clone(),
            self.options_store.clone(),
        )
        .await
        {
            Ok(mut data_file) => {
                if let Err(err) = self
                    .wal
                    .write()
                    .await
                    .log(&BlockDBLog::AddDataFile(data_file.id.clone()))
                    .await
                {
                    let _ = data_file.delete().await;
                    return Err(Error::Walr(err));
                }

                let data_file_id = data_file.id.clone();
                let data_file = Arc::new(RwLock::new(data_file));

                self.data_files
                    .write()
                    .await
                    .insert(data_file_id.clone(), data_file.clone());

                let data_block_id = data_file.write().await.write(bytes.as_ref()).await?;

                Ok(BlockKey {
                    data_file_id,
                    data_block_id,
                })
            }
            Err(err) => {
                delete_directory(&new_data_file_path).await?;

                Err(err)
            }
        }
    }
}