Struct Replica

Source
pub struct Replica<'a, D>
where D: Sized + Dispatch + Sync,
{ /* private fields */ }
Expand description

An instance of a replicated data structure. Uses a shared log to scale operations on the data structure across cores and processors.

Takes in one type argument: D represents the underlying sequential data structure D must implement the Dispatch trait.

A thread can be registered against the replica by calling register(). A mutable operation can be issued by calling execute_mut() (immutable uses execute). A mutable operation will be eventually executed against the replica along with any operations that were received on other replicas that share the same underlying log.

Implementations§

Source§

impl<'a, D> Replica<'a, D>
where D: Sized + Default + Dispatch + Sync,

Source

pub fn new<'b>( log: &Arc<Log<'b, <D as Dispatch>::WriteOperation>>, ) -> Arc<Replica<'b, D>>

Constructs an instance of a replicated data structure.

Takes a reference to the shared log as an argument. The Log is assumed to outlive the replica. The replica is bound to the log’s lifetime.

§Example
use node_replication::Dispatch;
use node_replication::Log;
use node_replication::Replica;

use std::sync::Arc;

// The data structure we want replicated.
#[derive(Default)]
struct Data {
    junk: u64,
}

// This trait allows the `Data` to be used with node-replication.
impl Dispatch for Data {
    type ReadOperation = ();
    type WriteOperation = u64;
    type Response = Option<u64>;

    // A read returns the underlying u64.
    fn dispatch(
        &self,
        _op: Self::ReadOperation,
    ) -> Self::Response {
        Some(self.junk)
    }

    // A write updates the underlying u64.
    fn dispatch_mut(
        &mut self,
        op: Self::WriteOperation,
    ) -> Self::Response {
        self.junk = op;
        None
    }
}

// First create a shared log.
let log = Arc::new(Log::<<Data as Dispatch>::WriteOperation>::default());

// Create a replica that uses the above log.
let replica = Replica::<Data>::new(&log);
Source§

impl<'a, D> Replica<'a, D>
where D: Sized + Dispatch + Sync,

Source

pub fn with_data<'b>( log: &Arc<Log<'b, <D as Dispatch>::WriteOperation>>, d: D, ) -> Arc<Replica<'b, D>>

Similar to Replica<D>::new, but we pass a pre-initialized data-structure as an argument (d) rather than relying on the Default trait to create one.

§Note

Replica<D>::new should be the preferred method to create a Replica. If with_data is used, care must be taken that the same state is passed to every Replica object. If not the resulting operations executed against replicas may not give deterministic results.

Source

pub fn register(&self) -> Option<ReplicaToken>

Registers a thread with this replica. Returns an idx inside an Option if the registration was successfull. None if the registration failed.

§Example
use node_replication::Dispatch;
use node_replication::Log;
use node_replication::Replica;

use std::sync::Arc;

#[derive(Default)]
struct Data {
    junk: u64,
}

impl Dispatch for Data {
    type ReadOperation = ();
    type WriteOperation = u64;
    type Response = Option<u64>;

    fn dispatch(
        &self,
        _op: Self::ReadOperation,
    ) -> Self::Response {
        Some(self.junk)
    }

    fn dispatch_mut(
        &mut self,
        op: Self::WriteOperation,
    ) -> Self::Response {
        self.junk = op;
        None
    }
}

let log = Arc::new(Log::<<Data as Dispatch>::WriteOperation>::default());
let replica = Replica::<Data>::new(&log);

// Calling register() returns an idx that can be used to execute
// operations against the replica.
let idx = replica.register().expect("Failed to register with replica.");
Source

pub fn execute_mut( &self, op: <D as Dispatch>::WriteOperation, idx: ReplicaToken, ) -> <D as Dispatch>::Response

Executes an mutable operation against this replica and returns a response. idx is an identifier for the thread performing the execute operation.

§Example
use node_replication::Dispatch;
use node_replication::Log;
use node_replication::Replica;

use std::sync::Arc;

#[derive(Default)]
struct Data {
    junk: u64,
}

impl Dispatch for Data {
    type ReadOperation = ();
    type WriteOperation = u64;
    type Response = Option<u64>;

    fn dispatch(
        &self,
        _op: Self::ReadOperation,
    ) -> Self::Response {
        Some(self.junk)
    }

    fn dispatch_mut(
        &mut self,
        op: Self::WriteOperation,
    ) -> Self::Response {
        self.junk = op;
        None
    }
}

let log = Arc::new(Log::<<Data as Dispatch>::WriteOperation>::default());
let replica = Replica::<Data>::new(&log);
let idx = replica.register().expect("Failed to register with replica.");

// execute_mut() can be used to write to the replicated data structure.
let res = replica.execute_mut(100, idx);
assert_eq!(None, res);
Source

pub fn execute( &self, op: <D as Dispatch>::ReadOperation, idx: ReplicaToken, ) -> <D as Dispatch>::Response

Executes a read-only operation against this replica and returns a response. idx is an identifier for the thread performing the execute operation.

§Example
use node_replication::Dispatch;
use node_replication::Log;
use node_replication::Replica;

use std::sync::Arc;

#[derive(Default)]
struct Data {
    junk: u64,
}

impl Dispatch for Data {
    type ReadOperation = ();
    type WriteOperation = u64;
    type Response = Option<u64>;

    fn dispatch(
        &self,
        _op: Self::ReadOperation,
    ) -> Self::Response {
        Some(self.junk)
    }

    fn dispatch_mut(
        &mut self,
        op: Self::WriteOperation,
    ) -> Self::Response {
        self.junk = op;
        None
    }
}

let log = Arc::new(Log::<<Data as Dispatch>::WriteOperation>::default());
let replica = Replica::<Data>::new(&log);
let idx = replica.register().expect("Failed to register with replica.");
let _wr = replica.execute_mut(100, idx);

// execute() can be used to read from the replicated data structure.
let res = replica.execute((), idx);
assert_eq!(Some(100), res);
Source

pub fn sync(&self, idx: ReplicaToken)

This method is useful when a replica stops making progress and some threads on another replica are still active. The active replica will use all the entries in the log and won’t be able perform garbage collection because of the inactive replica. So, this method syncs up the replica against the underlying log.

Trait Implementations§

Source§

impl<'a, D> Debug for Replica<'a, D>
where D: Sized + Sync + Dispatch,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'a, D> Sync for Replica<'a, D>
where D: Sized + Sync + Dispatch,

The Replica is Sync. Member variables are protected by a CAS on combiner. Contexts are thread-safe.

Auto Trait Implementations§

§

impl<'a, D> !Freeze for Replica<'a, D>

§

impl<'a, D> !RefUnwindSafe for Replica<'a, D>

§

impl<'a, D> Send for Replica<'a, D>
where D: Send, <D as Dispatch>::Response: Send,

§

impl<'a, D> Unpin for Replica<'a, D>
where D: Unpin, <D as Dispatch>::WriteOperation: Unpin, <D as Dispatch>::Response: Unpin,

§

impl<'a, D> !UnwindSafe for Replica<'a, D>

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, 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.