Hello everyone, I’ve recently been pondering a question in a project that utilizes Yjs, specifically, how to share (and of course, modify) the same YMap instance across multiple clients, ensuring that the YMap, once merged, maintains business-level semantic consistency at all times.
I understand that achieving this goal is impossible without imposing constraints on the contents of the YMap. Therefore, I will consider adhering to the following principles when modifying the YMap at the business layer:
- The keys in the YMap are definitely a fixed set and will not increase or decrease (for example, there will always only be ‘type’ and ‘content’ as the two keys).
- All modifications to this YMap instance by any client are performed within a single transaction.
- All modifications to this YMap instance by any client will involve setting both keys simultaneously; there will never be a situation where a client sets only ‘type’ and not ‘content’.
These principles are based on my understanding of the Yjs conflict resolution algorithm. If there are any wrong with these principles, please feel free to correct me.
Let’s discuss further using a YMap that only contains two fields: ‘type’ and ‘content’. In this YMap, the value of the ‘type’ field is a regular string, while the value of the ‘content’ field is a Y.AbstractType instance. The business layer will determine how to read the contents of ‘content’ based on different ‘type’ values (of course, there are also some ‘type’ values whose corresponding ‘content’ have the same format).
We assume that there are three types of values: type-A, type-B, and type-C. Both type-A and type-B share the same content format, while type-C has a different format.
When Bob wants to change the value of ‘type’ from type-A to type-B, in order to comply with the principles established earlier, although the value of ‘content’ remains consistent before and after the modification, Bob still needs to set the value of ‘content’ in the transaction. However, if Bob uses the original value for the set operation, it would actually lead to the content being cleared (since the old value is a Y.AbstractType instance). Of course, Bob could serialize the old content value and then reinitialize a new Y.AbstractType instance before assigning it to ‘content’, but this approach might result in the collaborative modifications to ‘content’ by clients being discarded.
const doc1 = new Y.Doc()
const map1 = doc1.getMap("M")
map1.set("type", "type-A")
map1.set("content", Y.Text("tttttt"))
doc1.transact((txn) => {
map1.set("type", "type-B")
map1.set("content", map1.get("content")) // <- actually the content will be cleared
})
map1.get("content").toString() // <- will be empty
So my question is, first, whether the principles assumed above are correct for ensuring consistency in this scenario? Second, how can Bob preserve the old value of ‘content’ in the transaction, while also obtaining the same logical status in Yjs’s conflict resolution process as if ‘content’ were reset?