Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Endless updating when using multiple browser windows #1

Open
ecssolutions opened this issue Nov 7, 2020 · 2 comments
Open

Endless updating when using multiple browser windows #1

ecssolutions opened this issue Nov 7, 2020 · 2 comments

Comments

@ecssolutions
Copy link

Hi,

First of all very cool plugin!

I just encountered one problem, wen the editor is opened in multiple windows, when the cursor is set both editors, it will trigger an endless loop of update content and setting cursor at the beginning of the editor. The loop can be stopped when the focus was moved out editor.

This can be reproduced by opening 2 windows of your codepen page https://codepen.io/thealmarques/full/YzqOrWV

@MingyuanZhang-hiretual
Copy link

MingyuanZhang-hiretual commented Feb 21, 2022

@ecssolutions This is because the plugin listens to the NodeChange event in plugin/src/main/ts/Plugin.ts:33

editor.on('NodeChange', (event: Event) => {
  const colab = collaborativeMap.get(editor.getParam('selector'));
  const user: User = JSON.parse(JSON.stringify(editor.getParam('budwriter')));
  if (colab) {
    colab.updateContent(event);
    colab.onListen(event, user);
  }
});

I removed this part, and we can add some other listeners we need. like:

editor.on('blur', (event: Event) => {
  const colab = collaborativeMap.get(editor.getParam('selector'));
  const user: User = JSON.parse(JSON.stringify(editor.getParam('budwriter')));
  if (colab) {
    colab.updateContent(event);
    colab.onListen(event, user);
  }
});

After doing that, you can always run yarn build to package your code.

@mikkorantalainen
Copy link

After trying to make this code to work in real world, I would say following:

You have to listen for both NodeChange and input events or at least of one of the following browsers will miss some events when you move cursor and start writing: Firefox, Chrome, Safari on iPad.

The problem you're seeing is actually caused by the overly simple protocol used in this plugin. It's a race condition and I fail to see anything in the protocol that could avoid the problem. If I've understood correctly, the issue is triggered at least in following case:

  1. Browser X loads the editor
  2. Browser Y loads the editor
  3. User of browser Y moves the cursor to some location (the important part here is that focus is in the TinyMCE editor area and text caret is somewhere in the middle of text).
  4. User of browser X moves the cursor to some other location and message set_cursor is sent
  5. Y receives the message update_cursor (message type is conveted by the server for some reason) and moves the cursor in some cases (I think this depends on the content inside the editor and maybe timing, too) the browser fires event SelectionChange through the TinyMCE which then causes Y to send message set_cursor.
  6. X receives the update_cursor message and does similar thing and ends up sending set_cursor again.
  7. The infinite loop happens because Y will handle the event like in step 4.

And depending on the events that the currently used browser fires on cursor movement or content changes, the protocol may end up sending duplicate or tripple messages for each event. I think this part could be fixed by adding a throttle limiter to event stream (I did at a limiter that it seemed to help a bit but didn't fix all the problems with cursor placement).

However, the protocol has even bigger problems. For example, if X and Y write (or otherwise execute any change to the content) at the same time, both will send message set_content with the content they currently have. As there's no locking or syncronization of any kind both may send conflicting version to the another user where that content then replaces the content in
the editor. And the same happens in mirror direction for the another user! After that, both see the content that the other user
has written and the change by the local user is lost. If they then try to fix the problem by retrying the end result depends on the order of events and with suitable timing, the versions never match and at least half of the user actions are lost for every mismatch.

If you test this code with a single computer only, it cannot show the most problems because you can have just one active caret and one focus position on most operating systems and as a result, you cannot see the case where both are: sending a change at the same time (here "same time" is closer in time than the sum of ping between the clients and the server).

And if there are more than 2 authors, the problem will just get worse because the end result depends on timing of all the events over all the clients and if different authors have different browsers (e.g. Firefox vs Chrome vs iPad Safari) the timing of events will be different for each client even if they were in the same network.

The sad fact is that you would really need to implement full CRDT algorithm with the cursor movement implemented on top of CRDT syncronization, too, if you want a collaborative editor without edge cases like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants