Y.Map conflict resolution: which side wins?

Is there a rule which can be easily understood by the user of a Y.Map, which entry will overwrite the other in the case of a merge conflict between two Y.Maps?

In my experiments, it is somewhat random which side wins (although both Y.Maps will be equal afterwards - and that’s definitely the more important part)

As you noticed, the eventual consistency is the important part.

I’m not sure what the internal rule is, but it might be something like whomever has the lower clientID or clock. It should be considered an implementation detail and not relied upon for business logic, since clients can manipulate these values.

I cannot be the clock which decides who wins as I waited for 100ms between filling my two Y.Maps - and that’s why I also developed my LWWMap which follows a “last-write-wins” strategy.

But clientId might be a good idea…

Lamport clock, not time clock. Was just a guess though. But you have more experience than me in that area :).

I am trying to do the exact same thing (write unit tests on a YMap with conflicts) and trying to predict the result. It does feel a little random, but I believe ClientID is used somehow…I am just not sure. Maybe @dmonad can point us to some code or give us a little more detail?

This code seems to predict it correctly:

// create a task and add it to one document (first edit)
 const { yTask, key } = createYTask();
    const yTasks = Converter.getYTasks(yDocClient);
    yTasks.set(key, yTask);

    const task = yTask.toJSON() as TaskData;
// Gotta be careful here and clone your memory.  YJS will just pass an object through.  This changes the original task and slightly modifies it
    task.meta = {
      creator: 9090089,

// Add the slightly modified task to a second document (first edit)
    const yTasks2 = Converter.getYTasks(yDocClient2);

    const { yTask: yTask2 } = createYTask(task);
    yTasks2.set(key, yTask2);

// Looks like higher client ID wins.
    const expectedTask = yDocClient.clientID > yDocClient2.clientID
      ? yTask
      : yTask2;

// This function validates the expected final state
    await validateApplyUpdatesForSave({
      upserts: {
        [key]: expectedTask.toJSON() as TaskData,
      deletes: [],