# Bitcasky [![Crates.io][crates-badge]][crates-url] [![MIT licensed][mit-badge]][mit-url] [![codecov][codecov-badge]][codecov-url] [![Build Status][actions-badge]][actions-url] [crates-badge]: https://img.shields.io/crates/v/bitcasky.svg [crates-url]: https://crates.io/crates/bitcasky [mit-badge]: https://img.shields.io/badge/license-MIT-yellow.svg [mit-url]: https://github.com/ylgrgyq/bitcasky/blob/master/LICENSE [codecov-badge]: https://codecov.io/gh/ylgrgyq/bitcasky/branch/master/graph/badge.svg [codecov-url]: https://app.codecov.io/gh/ylgrgyq/bitcasky/tree/master [actions-badge]: https://github.com/ylgrgyq/bitcasky/workflows/CI/badge.svg [actions-url]: https://github.com/ylgrgyq/bitcasky/actions?query=workflow%3ACI+branch%3Amaster Bitcasky is a Rust implementation of the Bitcask key-value store. It is an ACID-compliant, append-only key-value store that provides high write throughput. It is optimized for write-heavy workloads, and is commonly used for applications such as log storage and time-series data. ## Features - Append-only storage for durability and consistency - Memory-mapped files for efficient I/O - Key-value storage with O(1) read and write performance - Store expirable values ## Usage ### Basic usage To use Bitcasky, simply add it to your `Cargo.toml` file: ```rust [dependencies] bitcasky = "0.1.2" ``` Then, in your Rust code, import the `bitcasky` crate and start using the key-value store: ```rust use bitcasky::Bitcasky; fn main() { let mut db = Bitcasky::open("/path/to/db", BitcaskyOptions::default()).unwrap() db.put("key", "value").unwrap(); assert!(db.has("key").unwrap()); let value = db.get("key").unwrap().unwrap(); println!("{:?}", value); } ``` ### Store expirable value ```rust db.put_with_ttl("key", "value", Duration::from_secs(60)).unwrap(); // 60 seconds later assert!(db.get("key").unwrap().is_none()); ``` ### Delete some value or the entire database ```rust db.put("key1", "value1").unwrap(); db.put("key2", "value2").unwrap(); // delete some value db.delete("key1").unwrap(); assert!(db.get("key1").unwrap().is_none()); // drop database db.drop().unwrap(); assert!(db.get("key2").unwrap().is_none()); ``` ### Iterate database Iterate all keys. ```rust // iterate and print all keys in database bc.foreach_key(|k| println!("{}", String::from_utf8_lossy(k))).unwrap(); // fold all keys by concatenate them let ret = bc.fold_key( |k, accumulator: Option<String>| match accumulator { // concatenate new key to folded key string Some(folded_k) => Ok(Some(folded_k + &String::from_utf8_lossy(k))), // if we have not fold anything, use this new key as folded key None => Ok(Some(String::from_utf8_lossy(k).into())), }, // init accumulator None, ) .unwrap(); assert!(ret.is_some()); println!("{}", ret.unwrap()); ``` Iterate all keys and values. ```rust // iterate and print all keys and values in database bc.foreach(|k, v| { println!( "key: {}, value: {}", String::from_utf8_lossy(k), String::from_utf8_lossy(v) ) }) .unwrap(); // fold all keys and values by concatenate them let ret = bc .fold( |k, v, accumulator: Option<String>| match accumulator { // concatenate new key and value to folded values Some(folded_vals) => Ok(Some(format!( "{} key: {}, val: {};", folded_vals, String::from_utf8_lossy(k), String::from_utf8_lossy(v) ))), // if we have not fold anything, use this new key and value as folded values None => Ok(Some(format!( "key: {}, val: {};", String::from_utf8_lossy(k), String::from_utf8_lossy(v) ))), }, // init accumulator None, ) .unwrap(); assert!(ret.is_some()); println!("{}", ret.unwrap()); ``` ### Sync strategy By choosing a sync strategy, you can configure the durability of writes by specifying when to synchronize data to disk. The following sync strategies are available: * None — lets the operating system manage syncing writes * OSync — uses the O_SYNC flag, which forces syncs on every write * Time interval — sync at specified intervals (default: 60 secs) For example, create a Bitcasky database which sync on every 35 secs as follows: ```rust let db = Bitcasky::open( "/path/to/db", BitcaskyOptions::default().sync_strategy(SyncStrategy::Interval(Duration::from_secs(35))) ).unwrap(); ``` ### Merge process Bitcasky need to call merge periodically to reduce disk usage. The merge process traverses data files and reclaims space by eliminating out-of-date of deleted key/value pairs, writing only the current key/value pairs to a new set of files within the directory. Start merge process by calling `merge` on Bitcaksy instance like this: ```rust db.merge().unwrap(); ``` # License This project is licensed under the [MIT license]. [MIT license]: https://github.com/ylgrgyq/bitcasky/blob/master/LICENSE