> Each client can operate on its own what though?
Imagine the fundamental representation of your data structure is a sequence of operations. From those operations, you can generate your model. A really simple application of this is a graph, which is just a set of vertices and edges. So if you have these operations
VertexAdd { id: fooID, label: foo }
VertexAdd { id: barID, label: bar }
VertexAdd { id: quuxID, label: quux }
EdgeAdd { src: fooID, dst: quuxID }
where the ids generated are unique (say, uuids). The corresponding model is just the graph:
Graph {
vertices: {foo, bar, quux},
edges: {{foo, bar}},
}
You might imagine operations to remove vertices and edges as well. Many operations are commutative (add vertex), but some pairs of operations don't commute, e.g., add vertex/remove vertex. As I understand that, that's the central problem this paper is trying to address via CRDTs. What I'm saying is that the total ordering imposed by the server makes this concern moot, because the server always picks one sequential ordering.
Let's say there are multiple simultaneous clients editing our graph structure above at the same time. Let's consider an example of a pair of operations that commute. One of them decides to remove vertex quux while the other adds an edge that connects quux to foo:
client1:
VertexDelete { id: quuxID }
client2:
EdgeAdd { src: quuxID, dst: fooID }
If both clients attempt to update the server with these new operations, then the server will declare one ordering as true and send the resulting operations back to the clients with the correct ordering. The clients must then adjust. In this case, the outcome is the same regardless of the ordering chosen (because you can't draw an edge to a vertex that doesn't exist).
What about the case when two operations are not commutative? Well, that means they are dependent, which in turn means that every client receives them in the order in which they were applied. An add/delete vertex is one such example, because the client doing the delete must acquire a vertex ID to give to the VertexDelete operation, but the only way to acquire the ID is to observer the VertexAdd first.
Another example of dependent operations is attaching meta data to vertices, e.g.,
client1:
VertexAdd { id: anotherID, label: Another }
VertexLabel { id: anotherID, label: Changed }
In this case, VertexAdd/VertexLabel are dependent, but the Label operation requires the vertex ID.
Now of course, clients can misbehave. There's no reason why a client couldn't generate the ID and add operations in this order:
client1:
VertexLabel { id: anotherID, label: Changed }
VertexAdd { id: anotherID, label: Another }
But if we can assume well behaving clients, then this should be OK! And even if clients are misbehaving, then the process of turning the operations into a model can simply drop operations like VertexLabel because they reference a vertex that doesn't exist.