// The sync engine's messages, for a replica written in any language.
// PROTOCOL.md says what each one means and what a replica must do with it.
// The engine's Rust types (src/envelope/sync.rs) are generated from this file,
// and a test fails while they differ from what it generates.
syntax = "proto3";
package sync;
// A row's key: its parts as text, in primary key order — a uuid part
// lowercase and hyphenated, an integer part decimal.
message Key {
repeated string parts = 1;
}
// Everything the server sends a replica: one message, whichever it is.
message ToReplica {
// The version of the protocol the server speaks. A replica that receives a
// later one than its own does not act on it, and does not acknowledge it.
uint32 protocol = 1;
oneof body {
Down down = 2;
EndSnapshot end_snapshot = 3;
Filed filed = 4;
}
}
// One table's part of a message: rows to hold, then rows to let go.
message Table {
string table = 1;
// Rows to hold, each encoded as the table's own row.
repeated bytes upserts = 2;
// Rows to let go of.
repeated Key deletes = 3;
// The key of each upsert, in the same order, one per upsert: a side that
// cannot read a row can still say which one it was.
repeated Key keys = 4;
// Up only: the replica re-states these rows, of a setting or answers to
// wishes, as it holds them now (PROTOCOL.md section 7) — the server takes
// them whatever their version.
bool restated = 5;
// Down only, in a snapshot: the rows of a setting the server records this
// replica holding (PROTOCOL.md section 4.2) — the replica deletes, as its
// own write, each it does not hold.
repeated Key recorded = 6;
}
// Server to replica: rows to hold and rows to let go, in the order given —
// parents' upserts first, children's deletes first — applied in one
// transaction.
message Down {
// Set while the replica is provisioned from scratch: it remembers every
// row the snapshot names, and lets go of the rest when the snapshot ends.
string snapshot_id = 1;
repeated Table tables = 2;
// Rises with every message the server sends, from 1 to 2^63 - 1: a replica
// applies nothing at or below the last it applied from the same epoch.
uint64 sequence = 3;
// The server installation the sequence is from, a uuid.
string epoch = 4;
// The sequence of the message sent this replica before this one, in this
// epoch: a replica that did not apply that one missed it. 0 only on the
// message that begins a snapshot, which follows nothing.
uint64 previous = 5;
// A number of this message's own, drawn at random from 1 to 2^63 - 1: a
// server restored from an older copy numbers its messages again, and
// draws other nonces for them.
uint64 nonce = 6;
// The nonce of the message `previous` names: the one this follows is that
// sequence with that nonce. 0 when `previous` is 0.
uint64 previous_nonce = 7;
// The server's seal on this message for this replica, opaque to it: the
// replica gives it back as it came when it says it applied the message.
bytes mac = 8;
}
// Server to replica: a snapshot is complete. Every row of `tables` the
// replica holds and the snapshot did not name goes, children first: in
// reverse of the order `tables` lists them.
message EndSnapshot {
string snapshot_id = 1;
repeated string tables = 2;
// As Down's.
uint64 sequence = 3;
// As Down's.
string epoch = 4;
// As Down's: 0 when it ends a snapshot of nothing, which it begins.
uint64 previous = 5;
// As Down's.
uint64 nonce = 6;
uint64 previous_nonce = 7;
bytes mac = 8;
}
// Server to replica: these uploads are filed, or kept to be filed once they
// can be. The notes they carried are done.
message Filed {
repeated uint64 uploads = 1;
}
// Replica to server: rows it wrote, rows it could not take, what it holds,
// and the last message it applied. The transport says which replica sent it.
message Up {
reserved 1;
reserved "replica_id";
repeated Table tables = 2;
repeated Rejected rejected = 3;
// What the replica holds of each table sent down, to compare with what the
// server records it holds.
repeated Digest digests = 4;
// The last message the replica applied: its epoch, sequence, nonce and
// mac, as the message carried them. Set on the first lane when the server
// has not heard of it, and always with digests, which are only comparable
// once the replica applied every message recorded for it, and with a gap.
string epoch = 5;
uint64 sequence = 6;
uint64 nonce = 11;
bytes mac = 12;
// The number of this upload, when it carries rows: a fresh one on every
// send, drawn at random from 1 to 2^63 - 1, so no answer meant for another
// replica, or for this one before a restore, names it. The server answers
// with Filed once it filed it.
repeated uint64 uploads = 7;
// The version of the protocol the replica speaks.
uint32 protocol = 8;
// What the replica wrote that the server has not filed, when it says: on
// the first lane, when it changed. Absent: it did not say this time.
Backlog backlog = 9;
// It missed a message: the sequence of the newest one that arrived not
// following the last it applied, and that it therefore did not apply. 0:
// none. Said on the first lane, with the last applied, until a snapshot
// begins, and told again whenever it grows; the server answers with a
// snapshot, unless one that began after this sequence is on its way.
uint64 gap = 10;
}
// What a replica wrote that the server has not filed yet.
message Backlog {
repeated TableBacklog tables = 1;
}
// One table's part of a backlog.
message TableBacklog {
string table = 1;
// Rows not sent yet.
uint64 unsent = 2;
// Rows sent the server has not said it filed.
uint64 unfiled = 3;
// Rows that cannot go up as they are — larger than a message, refused by
// the transport, of a table or a key this build cannot read — kept until
// the replica runs again.
uint64 set_aside = 4;
}
// A row a replica could not take, or could not let go of, and why. The key
// is required.
message Rejected {
string table = 1;
Key key = 2;
string reason = 3;
// The message whose row, or delete, the replica refused: the server
// ignores a rejection of a message other than the last one that carried
// the row, or of another epoch.
string epoch = 4;
uint64 sequence = 5;
// The replica did not take the row because it deleted it, a row of its
// own, and the server has not filed the delete: no fault of the row's,
// nothing a fix cures. The server sends the row again, should it keep it.
bool held_back = 6;
}
// What a replica holds of one table: how many rows, and a fingerprint of
// their keys. Rows it retired, or rejected, are left out.
message Digest {
string table = 1;
uint64 count = 2;
uint64 fingerprint = 3;
}
// A row of the engine's table `sync_wishes`, which travels as any table
// written on both sides does: down in a `Down`'s tables, up in an `Up`'s. It
// is what the server wants one setting of the replica to hold: a row of a
// table the replica owns, whose desired columns the server may wish other
// values (PROTOCOL.md 4.5). The server owns the first fields; the replica the
// answer, which the server never sends down.
message Wish {
reserved 2;
reserved "replica_id";
// A uuid: the row's key.
string id = 1;
// The setting's table, and its row's key.
string table = 3;
Key key = 4;
// The row as wished, encoded as the setting's table's own row: the replica
// takes its desired columns, and compares its version when `if_unchanged`.
bytes wanted = 5;
// Drawn at random for every wish, from 1 to 2^63 - 1: an answer is of the
// wish whose version it names.
uint64 version = 6;
// Carried out only while the replica holds the version `wanted` carries.
bool if_unchanged = 7;
// The server's words on it: who asked, why.
string note = 8;
// The replica's answer: the version it answers, 0 for none yet.
uint64 answered = 9;
// "applied", "refused", "superseded" or "waiting"; empty with no answer.
string outcome = 10;
string reason = 11;
// When it answered, RFC 3339, by the replica's clock.
string answered_at = 12;
}