Skip to main content

MutationBuffer

Struct MutationBuffer 

Source
pub struct MutationBuffer { /* private fields */ }
Expand description

Write buffer for batching mutations with size limits.

MutationBuffer provides a memory-bounded container for accumulating mutations before applying them to a tree. This is useful for processing large datasets that don’t fit in memory, allowing you to flush mutations in batches.

§Size Tracking

The buffer tracks the total byte size of accumulated mutations:

  • For Upsert mutations: key length + value length
  • For Delete mutations: key length

When adding a mutation would exceed the configured maximum size, the add() method returns Err(Error::BufferFull).

§Example

use prolly::{MutationBuffer, Mutation, Error};

let mut buffer = MutationBuffer::with_max_size(1024); // 1KB limit

// Add mutations until buffer is full
let mutation = Mutation::Upsert {
    key: b"key".to_vec(),
    val: b"value".to_vec(),
};

buffer.add(mutation).unwrap();

// Check buffer state
assert!(!buffer.is_empty());
assert_eq!(buffer.len(), 1);

// Drain mutations for processing
let mutations = buffer.drain();
assert!(buffer.is_empty());

§Streaming Large Datasets

use prolly::{MutationBuffer, Mutation, Prolly, MemStore, Config};

let store = MemStore::new();
let prolly = Prolly::new(store, Config::default());
let mut tree = prolly.create();

let mut buffer = MutationBuffer::with_max_size(10 * 1024 * 1024); // 10MB

// Process large dataset in chunks
for i in 0..1000 {
    let mutation = Mutation::Upsert {
        key: format!("key{}", i).into_bytes(),
        val: format!("value{}", i).into_bytes(),
    };

    if buffer.add(mutation).is_err() {
        // Buffer full - flush to tree
        let mutations = buffer.drain();
        tree = prolly.batch(&tree, mutations).unwrap();
    }
}

// Flush remaining mutations
if !buffer.is_empty() {
    let mutations = buffer.drain();
    tree = prolly.batch(&tree, mutations).unwrap();
}

Implementations§

Source§

impl MutationBuffer

Source

pub fn new() -> Self

Create a new MutationBuffer with the default maximum size (64 MB).

§Returns

A new empty MutationBuffer with a 64 MB size limit.

§Example
use prolly::MutationBuffer;

let buffer = MutationBuffer::new();
assert!(buffer.is_empty());
Source

pub fn with_max_size(max_size: usize) -> Self

Create a new MutationBuffer with a custom maximum size.

§Arguments
  • max_size - Maximum buffer size in bytes
§Returns

A new empty MutationBuffer with the specified size limit.

§Example
use prolly::MutationBuffer;

let buffer = MutationBuffer::with_max_size(1024 * 1024); // 1MB limit
assert!(buffer.is_empty());
Source

pub fn add(&mut self, mutation: Mutation) -> Result<(), Error>

Add a mutation to the buffer.

The mutation’s size is calculated as:

  • Upsert: key length + value length
  • Delete: key length
§Arguments
  • mutation - The mutation to add
§Returns
  • Ok(()) - Mutation was added successfully
  • Err(Error::BufferFull) - Adding the mutation would exceed the buffer’s max size
§Example
use prolly::{MutationBuffer, Mutation, Error};

let mut buffer = MutationBuffer::with_max_size(5);

// This fits (1 + 1 = 2 bytes)
let result = buffer.add(Mutation::Upsert {
    key: b"a".to_vec(),
    val: b"1".to_vec(),
});
assert!(result.is_ok());

// This would exceed the limit (3 + 5 = 8 bytes, total would be 10 > 5)
let result = buffer.add(Mutation::Upsert {
    key: b"key".to_vec(),
    val: b"value".to_vec(),
});
assert!(matches!(result, Err(Error::BufferFull)));
Source

pub fn drain(&mut self) -> Vec<Mutation>

Drain all mutations from the buffer and reset its state.

Returns all accumulated mutations and resets the buffer to empty. The current size is reset to 0.

§Returns

A vector containing all mutations that were in the buffer.

§Example
use prolly::{MutationBuffer, Mutation};

let mut buffer = MutationBuffer::new();
buffer.add(Mutation::Upsert {
    key: b"key".to_vec(),
    val: b"value".to_vec(),
}).unwrap();

let mutations = buffer.drain();
assert_eq!(mutations.len(), 1);
assert!(buffer.is_empty());
assert_eq!(buffer.size(), 0);
Source

pub fn is_full(&self) -> bool

Check if the buffer is full.

Returns true if the current size equals or exceeds the maximum size. Note that this doesn’t guarantee the next add() will fail, as it depends on the size of the mutation being added.

§Returns

true if the buffer is at or over capacity, false otherwise.

§Example
use prolly::{MutationBuffer, Mutation};

let mut buffer = MutationBuffer::with_max_size(5);
assert!(!buffer.is_full());

buffer.add(Mutation::Upsert {
    key: b"ab".to_vec(),
    val: b"cde".to_vec(),
}).unwrap();
assert!(buffer.is_full()); // 2 + 3 = 5 bytes
Source

pub fn size(&self) -> usize

Get the current size of the buffer in bytes.

§Returns

The total byte size of all mutations in the buffer.

§Example
use prolly::{MutationBuffer, Mutation};

let mut buffer = MutationBuffer::new();
assert_eq!(buffer.size(), 0);

buffer.add(Mutation::Upsert {
    key: b"key".to_vec(),   // 3 bytes
    val: b"value".to_vec(), // 5 bytes
}).unwrap();
assert_eq!(buffer.size(), 8);
Source

pub fn len(&self) -> usize

Get the number of mutations in the buffer.

§Returns

The count of mutations currently in the buffer.

§Example
use prolly::{MutationBuffer, Mutation};

let mut buffer = MutationBuffer::new();
assert_eq!(buffer.len(), 0);

buffer.add(Mutation::Upsert {
    key: b"key".to_vec(),
    val: b"value".to_vec(),
}).unwrap();
assert_eq!(buffer.len(), 1);
Source

pub fn is_empty(&self) -> bool

Check if the buffer is empty.

§Returns

true if the buffer contains no mutations, false otherwise.

§Example
use prolly::{MutationBuffer, Mutation};

let mut buffer = MutationBuffer::new();
assert!(buffer.is_empty());

buffer.add(Mutation::Upsert {
    key: b"key".to_vec(),
    val: b"value".to_vec(),
}).unwrap();
assert!(!buffer.is_empty());
Source

pub fn sort(&mut self)

Sort mutations by key in lexicographic byte order.

Sorts mutations in place using lexicographic byte ordering, which is consistent with the tree’s key ordering. This prepares mutations for efficient batch processing.

The sort is stable, meaning mutations with the same key maintain their relative order. This is important for last-write-wins semantics when combined with deduplication.

§Example
use prolly::{MutationBuffer, Mutation};

let mut buffer = MutationBuffer::new();
buffer.add(Mutation::Upsert {
    key: b"c".to_vec(),
    val: b"3".to_vec(),
}).unwrap();
buffer.add(Mutation::Upsert {
    key: b"a".to_vec(),
    val: b"1".to_vec(),
}).unwrap();
buffer.add(Mutation::Upsert {
    key: b"b".to_vec(),
    val: b"2".to_vec(),
}).unwrap();

buffer.sort();

let mutations = buffer.drain();
assert_eq!(mutations[0].key(), b"a");
assert_eq!(mutations[1].key(), b"b");
assert_eq!(mutations[2].key(), b"c");

Trait Implementations§

Source§

impl Default for MutationBuffer

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.