OpMove

Struct OpMove 

Source
pub struct OpMove<ID: TreeId, TM: TreeMeta, A: Actor> { /* private fields */ }
Expand description

Implements OpMove, the only way to manipulate tree data.

OpMove are applied via State::apply_op() or at a higher level via TreeReplica::apply_op()

§From the paper[1]:

We allow the tree to be updated in three ways: by creating a new child of any parent node, by deleting a node, or by moving a node to be a child of a new parent. However all three types of update can be represented by a move operation. To create a node, we generate a fresh ID for that node, and issue an operation to move this new ID to be created. We also designate as “trash” some node ID that does not exist in the tree; then we can delete a node by moving it to be a child of the trash.

Thus, we define one kind of operation: Move t p m c. A move operation is a 4-tuple consisting of a timestamp t of type ’t, a parent node ID p of type ’n, a metadata field m of type ’m, and a child node ID c of type ’n. Here, ’t, ’n, and ’m are type variables that can be replaced with arbitrary types; we only require that node identifiers ’n are globally unique (eg UUIDs); timestamps ’t need to be globally unique and totally ordered (eg Lamport timestamps [11]).

The meaning of an operation Move t p m c is that at time t, the node with ID c is moved to be a child of the parent node with ID p. The operation does not specify the old location of c; the algorithm simply removes c from wherever it is currently located in the tree, and moves it to p. If c does not currently exist in the tree, it is created as a child of p.

The metadata field m in a move operation allows additional information to be associated with the parent-child relationship of p and c. For example, in a filesystem, the parent and child are the inodes of a directory and a file within it respectively, and the metadata contains the filename of the child. Thus, a file with inode c can be renamed by performing a Move t p m c, where the new parent directory p is the inode of the existing parent (unchanged), but the metadata m contains the new filename.

§When users want to make changes to the tree on their local replica they generate new Move t p m c operations for these changes, and apply these operations using the algorithm described…

[1] https://martin.kleppmann.com/papers/move-op.pdf

Implementations§

Source§

impl<ID: TreeId, TM: TreeMeta, A: Actor> OpMove<ID, TM, A>

Source

pub fn new( timestamp: Clock<A>, parent_id: ID, metadata: TM, child_id: ID, ) -> Self

create a new OpMove instance

Source

pub fn timestamp(&self) -> &Clock<A>

returns timestamp reference

Examples found in repository?
examples/demo.rs (line 341)
278fn demo_move_to_trash() {
279    // pass true flag to enable causally stable threshold tracking
280    let mut r1: TreeReplica<TypeId, TypeMeta, TypeActor> = TreeReplica::new(new_id());
281    let mut r2: TreeReplica<TypeId, TypeMeta, TypeActor> = TreeReplica::new(new_id());
282
283    let ids: HashMap<&str, TypeId> = [
284        ("forest", new_id()),
285        ("trash", new_id()),
286        ("root", new_id()),
287        ("home", new_id()),
288        ("bob", new_id()),
289        ("project", new_id()),
290    ]
291    .iter()
292    .cloned()
293    .collect();
294
295    // Generate initial tree state.
296    //
297    // - forest
298    //   - trash
299    //   - root
300    //     - home
301    //       - bob
302    //         - project
303    let mut ops = vec![
304        (ids["forest"], "root", ids["root"]),
305        (ids["forest"], "trash", ids["trash"]),
306        (ids["root"], "home", ids["home"]),
307        (ids["home"], "bob", ids["bob"]),
308        (ids["bob"], "project", ids["project"]),
309    ];
310
311    // add some nodes under project
312    mktree_ops(&mut ops, &mut r1, ids["project"], 2, 3);
313    let opmoves = r1.opmoves(ops);
314    r1.apply_ops_byref(&opmoves);
315    r2.apply_ops_byref(&opmoves);
316
317    println!("Initial tree");
318    print_tree(r1.tree(), &ids["forest"]);
319
320    // move project to trash
321    let ops = vec![r1.opmove(ids["trash"], "project", ids["project"])];
322    r1.apply_ops_byref(&ops);
323    r2.apply_ops_byref(&ops);
324
325    println!("\nAfter project moved to trash (deleted) on both replicas");
326    print_tree(r1.tree(), &ids["forest"]);
327
328    // Initially, trashed nodes must be retained because a concurrent move
329    // operation may move them back out of the trash.
330    //
331    // Once the operation that moved a node to the trash is causally
332    // stable, we know that no future operations will refer to this node,
333    // and so the trashed node and its descendants can be discarded.
334    //
335    // note:  change r1.opmoves() to r2.opmoves() above to
336    //        make the causally stable threshold less than the trash operation
337    //        timestamp, which will cause this test to fail, ie hit the
338    //        "trash should not be emptied" condition.
339    let result = r2.causally_stable_threshold();
340    match result {
341        Some(cst) if cst < ops[0].timestamp() => {
342            println!(
343                "\ncausally stable threshold:\n{:#?}\n\ntrash operation:\n{:#?}",
344                cst,
345                ops[0].timestamp()
346            );
347            panic!("!error: causally stable threshold is less than trash operation timestamp");
348        }
349        None => panic!("!error: causally stable threshold not found"),
350        _ => {}
351    }
352
353    // empty trash
354    r1.tree_mut().rm_subtree(&ids["trash"], false);
355    println!("\nDelete op is now causally stable, so we can empty trash:");
356    print_tree(r1.tree(), &ids["forest"]);
357}
Source

pub fn parent_id(&self) -> &ID

returns parent_id reference

Source

pub fn metadata(&self) -> &TM

returns metadata reference

Source

pub fn child_id(&self) -> &ID

returns child_id reference

Trait Implementations§

Source§

impl<ID: TreeId + Arbitrary, A: Actor + Arbitrary, TM: TreeMeta + Arbitrary> Arbitrary for OpMove<ID, TM, A>

Source§

fn arbitrary<G: Gen>(g: &mut G) -> Self

generates an arbitrary (random) OpMove

Source§

fn shrink(&self) -> Box<dyn Iterator<Item = Self>>

Source§

impl<ID: Clone + TreeId, TM: Clone + TreeMeta, A: Clone + Actor> Clone for OpMove<ID, TM, A>

Source§

fn clone(&self) -> OpMove<ID, TM, A>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<ID: Debug + TreeId, TM: Debug + TreeMeta, A: Debug + Actor> Debug for OpMove<ID, TM, A>

Source§

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

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

impl<'de, ID, TM, A> Deserialize<'de> for OpMove<ID, TM, A>
where ID: Deserialize<'de> + TreeId, TM: Deserialize<'de> + TreeMeta, A: Deserialize<'de> + Actor,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<ID: TreeId, A: Actor, TM: TreeMeta> From<LogOpMove<ID, TM, A>> for OpMove<ID, TM, A>

Source§

fn from(l: LogOpMove<ID, TM, A>) -> Self

creates OpMove from a LogOpMove

Source§

impl<ID: Hash + TreeId, TM: Hash + TreeMeta, A: Hash + Actor> Hash for OpMove<ID, TM, A>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<ID: PartialEq + TreeId, TM: PartialEq + TreeMeta, A: PartialEq + Actor> PartialEq for OpMove<ID, TM, A>

Source§

fn eq(&self, other: &OpMove<ID, TM, A>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<ID, TM, A> Serialize for OpMove<ID, TM, A>
where ID: Serialize + TreeId, TM: Serialize + TreeMeta, A: Serialize + Actor,

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl<ID: Eq + TreeId, TM: Eq + TreeMeta, A: Eq + Actor> Eq for OpMove<ID, TM, A>

Source§

impl<ID: TreeId, TM: TreeMeta, A: Actor> StructuralPartialEq for OpMove<ID, TM, A>

Auto Trait Implementations§

§

impl<ID, TM, A> Freeze for OpMove<ID, TM, A>
where ID: Freeze, TM: Freeze, A: Freeze,

§

impl<ID, TM, A> RefUnwindSafe for OpMove<ID, TM, A>

§

impl<ID, TM, A> Send for OpMove<ID, TM, A>
where ID: Send, TM: Send, A: Send,

§

impl<ID, TM, A> Sync for OpMove<ID, TM, A>
where ID: Sync, TM: Sync, A: Sync,

§

impl<ID, TM, A> Unpin for OpMove<ID, TM, A>
where ID: Unpin, TM: Unpin, A: Unpin,

§

impl<ID, TM, A> UnwindSafe for OpMove<ID, TM, A>
where ID: UnwindSafe, TM: UnwindSafe, A: UnwindSafe,

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> Member for T
where T: Clone + Hash + Eq,

Source§

impl<ID> TreeId for ID
where ID: Eq + Clone + Hash,

Source§

impl<TM> TreeMeta for TM
where TM: Clone,