1 Copyright 2020-2021 Ian Jackson and contributors to Otter
2 SPDX-License-Identifier: AGPL-3.0-or-later
6 CONCURRENT UPDATE PROTOCOL AND ANALYSIS
7 =======================================
9 (Assumption: our network techniques deliver messages in order in both
10 directions between a server and a client; failure of the communication
11 stream is allowed to break things - we treat it as fatal.)
13 We run the following algorithm / data model separately for each piece.
21 * actual committed history, a list of updates
22 * the history seen so far by the client
23 this can only advance, which is a prefix of the above
24 * possibly branch(es) of history made by the client;
25 this can rewind and be discarded
27 There is a diagraph of updates. Each update specifies the complete
28 state of the piece. The server maintains an authoritative history;
29 the client only maintains the state of the piece.
31 Updates may be from this client ("`Client` updates") or from another
32 client or made by the server itself. We look at the protocol from a
33 single client's point of view, and call all the other updates
36 Each update has a single direct ancestor, but this is not recorded in
37 the message nor by the client. Updates may be some combination of:
39 * `Recorded` - stored by server.
40 * `Downbound` - on way from server to client.
41 * `Discarded` - received by server and thrown away.
42 * `Upbound` - on way from client to server.
43 * `Displayed` - seen by client, is most recent state there
44 * `Superseded` - seen by client, is no longer most recent state.
46 Server assigns generation number (strictly increasing but maybe with
47 gaps) to each `Recorded` update.
49 Each `Upbound` update is tagged with the generation of the most recent
52 (Actually the most recent Processed generation stored by the client is
53 global across all pieces, and the server's generations are likewise
54 generated globally although recorded separately for each piece. So
55 the update contain s a generation >= the most recently Processed
56 update for that piece, and < any subseuent Processed upate for that
57 piece. The server only compares the incoming update generation for >=
58 with a recorded value, so this makes no difference.)
60 Desirable properties and invariants
61 -----------------------------------
63 Legal combinations are:
65 * `Server` + `Recorded` + `Downbound`
66 * `Server` + `Recorded` + `Displayed`
67 * `Server` + `Recorded` + `Superseded`
68 * `Client` + (`Displayed` / `Superseded`) + `Upbound`
69 * `Client` + (`Displayed` / `Superseded`) + `Recorded`
70 * `Client` + (`Displayed` / `Superseded`) + `Discarded`
72 _linear_: The `Recorded` updates form a linear sequence (so it is only
75 _forwards_: Each update has at most one `Server` descendant, and at most
76 one `Client` descendant.
78 _display_: Exactly one update is `Displayed` at any one time. It has no
79 `Client` child and no `Superseded` descendants. Each update is only
80 `Displayed` at most once, and can only become `Superseded` after having
83 _discard_: For every `Discarded` update K there is a `Recorded` `Server`
84 update S which not an ancestor of K; and with S's generation > K's
87 _record_: Sequence of `Recorded` updates is as follows:
89 * `Recorded` `Superseded` (zero or more)
90 * `Recorded` `Displayed` (zero or one)
91 * `Recorded` `Downbound` (zero or more)
94 Update generation operations
95 ----------------------------
97 ### I. Server update ###
99 Server may invent and transmit an update at any time. Its ancestor is
100 the most recent `Recorded` update. It is `Recorded`+`Downbound`. No other
101 updates change their state.
103 Properties and invariants: _linear_, _forwards_, _record_ are
104 obvious. _display_ is not affected.
106 ### II. Client update ###
108 Client may invent and transmit an update at any time; it must have the
109 `Displayed` update as its parent. That update becomes `Superseded`.
110 The new update is `Displayed`+`Upbound`.
112 _linear_ and _record_ are not affected. _forwardxs_ is honoured
113 because of _display_. _display_ is preserved.
115 Server message reception
116 ------------------------
118 Suppose the server receives a message, Q. It was `Client` `Upbound`.
120 Let T be the most recent `Recorded` message. The server finds the most
121 recent `Server` `Recorded` message, U.
123 ### I. Q's generation is >= U's generation. ###
125 The server records Q with T as parent. Q becomes `Client` `Recorded`; it
126 is `Displayed` or `Superseded` as before.
128 _linear_: We need to show that T really is Q's parent.
130 If Q's generation >= U's generation, the ancestry path <U..Q] exists
131 and contains only `Client` updates: if there were some `Server` update S
132 in U..Q, it would be `Recorded` (since all `Server` messages are) and more
133 recent than U, contradicting the definition of U.
135 Because of in-order message delivery, all updates in <U..Q> have been
136 received by the server. So they must be `Recorded` or `Discarded`.
138 Suppose one, K, was `Discarded`. Then by _discard_ there is some
139 `Recorded` `Server` update S which is not an ancestor of Q. S's
140 generation must be > Q's tag, since Q's tag is >= all of its `Recorded`
141 ancestors. So S's generation > U's. But this means that S is a
142 `Server` update more recent than U. _|_
144 So everything in <U..Q> is `Recorded`. Only Q is not `Recorded`.
145 Therefore Q's parent is indeed the most recent `Recorded` update, T.
147 _forwards_: We aren't making new messages. _display_: We aren't
148 changing any of this. _discard_: we aren't discarding anything.
150 _record_: Let S be the most recent `Recorded` `Downbound` update S. This
151 must be U. But Q's generation >= U's generation so U can no longer be
152 `Downbound`. So there are no `Downbound` updates. Maybe Q is `Displayed`;
153 in any case all older `Recorded` updates are `Superseded`.
155 ### II. Q's generation is < U's generation ###
157 The server discards Q. It becomes `Discarded`.
159 _linear_, _forwards_, _display_, _record_: No change.
161 _discard_: Q becomes K. Use U as S. U is `Recorded`. If Q had U as an
162 ancestor, U's generation would have been visible to the client when it
163 generated Q, so Q's tag would have been >= U's generation. S's
164 generation is U's generation and is >= Q's by case assumption.
167 Client message reception
168 ------------------------
170 Suppose the client receives a message Q.
172 It displays it. (The currently `Displayed` update becomes `Superseded`.
173 Q becomes `Displayed`.)
175 _linear_, _forwards_, discard_: No change.
177 _display_: Q is a `Server` update because it was `Downbound`. We have
178 just received it so it cannot yet have any `Client` descendant. It
179 might have `Server` descendants, but those are also `Recorded` `Downbound`,
182 _record_: By order of delivery, Q is the most recent `Recorded`
189 "When things ahve settled, all is consistent."
191 If there are no `Upbound` or `Downbound` updates, the most recent `Recorded`
192 update R is `Displayed`.
194 Suppose some update D is displayed instead.
196 D can't be `Recorded`. If it were, R would have to be `Downbound`, by
199 So D can't be `Server`. Suppose D is `Client`. It must be `Recorded`,
200 `Upbound` or `Discarded`. Only `Discarded` is left.
202 If D is `Discarded`, by _discard_, there is some `Recorded` `Server` S which
203 is not an ancestor of D, with S's generation > D's. S must be
204 `Displayed` or `Superseded`, since there are no `Downbound` updates. If S
205 were `Displayed` it would be equal to D, hence D would be an ancestor of
206 S. So S must be `Superseded`.
208 If S is `Recorded` `Superseded`, at some point it was `Displayed`. D is now
209 `Displayed`. So D was `Displayed` more recently than S. D is `Client` so
210 when it was `Displayed` it became `Upbound`, tagged with a generation of
211 at least S's; i.e. D's generation >= S's. _|_
216 Actually, the client wants to know when conflicts occur, so that it
217 can report to the user, and interrupt drag operations etc.
220 Update messages from the client to the server also contain a client
221 sequence number (monotonically but not strictly increasing). When the
222 client sends an update, it makes a note of the sequence number. This
223 is the "outstanding Client updates sequence number note", or the
226 When the server processes a message from the client and Records it, it
227 puts the client sequence number in the update stream (as a non-update
228 message). (This is skipped if it's the same as the last client
229 sequence number for that client.)
231 If the echoed sequence number is equal to the client's oustanding
232 note, the client knows the server is up to date, and deletes the note.
234 If the client sees a Server update message, and the client has a note,
235 it knows that there was a conflict.
241 Each piece has a Z level which is a finite f64, set by the client
242 which manipulates the piece, according to the protocol above.
244 Each piece *also* has a Z level generation. This is set by the
245 server. The server guarantees to set it to the server generation, and
246 guarantees to do so as the result of any client Z level update.
248 So the client which sends a Z level update can assume that a server
249 update to the generation will turn up, and with a higher value.
251 The Z generation is used to disambiguate the Z order for pieces with
252 identical Z level. Higher values are closer to the user (ie, occlude