/// <summary> /// Called on the data heap server nodes during replication, performs CRDT merge operation by /// taking the local state represented by this instance and merging another version(s) which came from other heap nodes. /// The function returns a result of the merge (conflict-free by definition) which is then used as the most /// current state instance, or null if this instance already represents the most current instance. /// WARNING: Per CvRDT definition, this operation is COMMUTATIVE, ASSOCIATIVE, and IDEMPOTENT. /// Failure to comply with these requirements may result in an infinite inter-node rotary traffic pattern. /// </summary> /// <param name="node">Node where data change takes place</param> /// <param name="space">Space where this object is stored</param> /// <param name="others">Other versions got form other nodes</param> /// <returns>Object instance which results from merge or null if THIS instance already represents the latest eventual state and no changes are necessary</returns> protected virtual HeapObject DoCrdt_Merge(IServerNodeContext node, ISpace space, IEnumerable <HeapObject> others) { var result = this; foreach (var ver in others) { if (ver.Sys_VerUtc > result.Sys_VerUtc) { result = ver; } } return(result == this ? null : result); }
/// <summary> /// Called by the data heap server nodes during replication, performs CRDT merge operation by /// taking the local state represented by this instance and merging another version(s) which came from other heap nodes. /// The function returns a result of the merge (conflict-free by definition) which is then used as the most /// current state instance, or null if this instance already represents the most current instance. /// WARNING: Per CvRDT definition, this operation is COMMUTATIVE, ASSOCIATIVE, and IDEMPOTENT. /// Failure to comply with these requirements may result in an infinite inter-node rotary traffic pattern. /// </summary> /// <param name="node">Node where data change takes place</param> /// <param name="space">Space where this object is stored</param> /// <param name="others">Other versions got form other nodes</param> /// <returns> /// Object instance which results from merge or null if THIS instance already represents the latest eventual state and no changes are necessary. /// You either return null, or one of "others" OR you can return a brand new object (not this or others), in which case the system treats it a as /// a brand new version performing necessary version stamping via `Crdt_Set()` /// </returns> internal HeapObject Crdt_Merge(IServerNodeContext node, ISpace space, IEnumerable <HeapObject> others) { if (others == null) { return(null); } if (!others.Any()) { return(null); } var t = this.GetType(); others.IsTrue(v => v.All(one => one.GetType() == t && one.Sys_Id == this.Sys_Id), "Non empty version for the same Sys_Id"); return(DoCrdt_Merge(node, space, others).IsTrue(r => r == null || r.GetType() == t, "Returned type mismatch")); }
/// <summary> /// Called by the server node, "seals" the data version by stamping appropriate object attributes. /// The node calls this method when the data is SET(Updated), but typically NOT when it is synchronized/merged /// unless a merge yields a brand new version of data. /// You must always call the base implementation /// </summary> /// <param name="node">Node where data change takes place</param> /// <param name="space">Space where this object is stored</param> /// <param name="newState">The new version state</param> /// <remarks> /// This is an extension point for any kind of CRDT mechanism, e.g. you can use vector clocks by /// storing the appropriate value in your derived type fields and then extend this method to populate the object version /// accordingly. /// </remarks> protected internal virtual void Crdt_Set(IServerNodeContext node, ISpace space, State newState) { Sys_VerState = newState; Sys_VerUtc = node.UtcNow.ToMillisecondsSinceUnixEpochStart(); Sys_VerNode = node.Node.NodeId; }