| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| syntax = "proto3"; |
| |
| package ambient.mdp.submerge; |
| |
| // A Submerge Document. |
| // |
| // This is the serialized version of `submerge::Document` in Rust, primarily |
| // used as the format for persistent storage. |
| // |
| // Next ID: 4 |
| message Document { |
| // The ID of nodes in this document. Because the node IDs tend to be repeated |
| // many times in a document, for example in vector clocks, and they may be |
| // long like 36 character UUIDs, they are extracted out to the top level. The |
| // rest of this document then refers to the nodes by their index in this |
| // node_ids list. |
| repeated string node_ids = 1; |
| // The version of the document. This is used to compare against incoming |
| // DeltaDocuments (a.k.a. update messages) to determine whether the update is |
| // ready to be applied. |
| CompressedVectorClock version = 2; |
| // The root of the data tree. |
| SubmergeContainer root = 3; |
| } |
| |
| // A Submerge Delta Document. |
| // |
| // This is also known as an update message. Corresponds to the |
| // `submerge::DeltaDocument` struct in Rust, and is primarily used as the format |
| // for network updates. |
| // |
| // Next ID: 5 |
| message DeltaDocument { |
| // The ID of nodes in this document. Because the node IDs tend to be repeated |
| // many times in a document, for example in vector clocks, and they may be |
| // long like 36 character UUIDs, they are extracted out to the top level. The |
| // rest of this document then refers to the nodes by their index in this |
| // node_ids list. |
| repeated string node_ids = 1; |
| // The version a document will be at after applying this delta. |
| CompressedVectorClock version = 2; |
| // The base_version of this delta document. Information that hasn't changed |
| // since this base_version won't be part of this delta, so Submerge documents |
| // will verify that it is as least as new as the base before applying this |
| // delta. |
| CompressedVectorClock base_version = 3; |
| // The root of the data tree, only containing delta information that has |
| // changed since base_version. |
| SubmergeContainer root = 4; |
| } |
| |
| // A container in Submerge that contains one of Submerge's CRDT types. |
| // |
| // When thinking of a Submerge document as a tree of CRDTs, each container can |
| // be thought of as a "node" (although the term "node" is generally not used in |
| // Submerge to describe this, and is instead reserved to describe the clients or |
| // actors making changes to a document). |
| // |
| // Corresponds to the `LwwCrdtContainer` type in Rust. |
| // |
| // Next ID: 8 |
| message SubmergeContainer { |
| // The node that made the last update to the type of this container. |
| // |
| // The value is the index of the node in `node_ids` of Document or |
| // DeltaDocument. |
| optional uint32 updater = 1; |
| // The timestamp tracking when the container type was last updated (excluding |
| // any changes to the children). |
| HybridLogicalTimestamp timestamp = 2; |
| // The version vector tracking when this subtree (this node or any of its |
| // descendants) were last updated. |
| CompressedVectorClock subtree_version = 3; |
| |
| // The CRDT value contained in this container. |
| // |
| // Note: This value is optional; zero or one of the following fields can be |
| // set in valid constructions of this message. |
| oneof value { |
| SubmergeMap map = 4; |
| SubmergeSet set = 5; |
| SubmergeRegister register = 6; |
| SubmergeVectorData vector_data = 7; |
| } |
| } |
| |
| // A map of Submerge containers. |
| // |
| // Corresponds to the `LwwMap` type in Rust. |
| // |
| // Next ID: 2 |
| message SubmergeMap { |
| map<string, SubmergeContainer> elements = 1; |
| } |
| |
| // A set of values. |
| // |
| // Corresponds to the `Set` type in Rust. |
| // |
| // Next ID: 2 |
| message SubmergeSet { |
| // Next ID: 4 |
| message SetElement { |
| bytes value = 1; |
| uint32 generation = 2; |
| bool removed = 3; |
| } |
| |
| repeated SetElement elements = 1; |
| } |
| |
| // A register containing a "single" atomically updated value. |
| // |
| // "Single" is in quotes here because the value type can be a compound type or a |
| // collection, but the register's merging logic will treat it as one unit, and |
| // apply any updates atomically without merging. |
| // |
| // Corresponds to the `Register` type in Rust. |
| // |
| // Next ID: 2 |
| message SubmergeRegister { |
| // Next ID: 4 |
| message RegisterElement { |
| optional bytes value = 1; |
| CompressedVectorClock vector_clock = 2; |
| HybridLogicalTimestamp hlc = 3; |
| } |
| |
| repeated RegisterElement elements = 1; |
| } |
| |
| // A vector data type. |
| // |
| // This type contains an area for each node to write to, and is therefore |
| // guaranteed to merge without conflicts. |
| // |
| // Corresponds to the `VectorData` type in Rust. |
| // |
| // Next ID: 2 |
| message SubmergeVectorData { |
| // Next ID: 4 |
| message VecElement { |
| uint32 node = 1; |
| optional bytes value = 2; |
| uint32 version = 3; |
| } |
| |
| repeated VecElement elements = 1; |
| } |
| |
| // Timestamp types |
| |
| // A compressed vector clock or version vector. |
| // |
| // Because the node IDs tend to be repeated many times in a document, and they |
| // may be long, like 36 character UUIDs, they are extracted out to the top level |
| // of a document. The nodes in this vector clock refers to the index of a node ID |
| // in the `node_ids` list in the `Document` or `DeltaDocument` types. If a self- |
| // contained version of vector clock is needed, use `VectorClock` instead. |
| // |
| // Despite the name, this message can be used as both vector clocks and version |
| // vectors, depending on the usage. |
| // |
| // Corresponds to the `VectorClock` type in Rust. |
| // |
| // Next ID: 3 |
| message CompressedVectorClock { |
| repeated uint32 nodes = 1 [packed = true]; |
| repeated uint32 values = 2 [packed = true]; |
| } |
| |
| // A hybrid logical timestamp. |
| // |
| // This timestamp tracks both wall-clock time and causality. Sorting entries by |
| // this timestamp will result in an order consistent with causality with a bias |
| // towards tie-breaking with wall-clock time. |
| // |
| // Corresponds to the `UnnamedHybridLogicalTimestamp` type in Rust. |
| // |
| // Next ID: 3 |
| message HybridLogicalTimestamp { |
| uint64 logical_time = 1; |
| uint32 causality = 2; |
| } |
| |
| |
| // A vector clock or version vector. |
| // |
| // Despite the name, this message can be used as both vector clocks and version |
| // vectors, depending on the usage. |
| // |
| // This message is self-contained and can be directly sent to remote devices. |
| // |
| // Next ID: 2 |
| message VectorClock { |
| // A map from the node ID to the clock value for that node. |
| map<string, uint32> node_clock_values = 1; |
| } |
| |