blob: 70b25bb1fb80d263c715d4d11a5b32049144dd7b [file] [log] [blame]
// 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;
}