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

Unsure how to fallback gracefully from initial provider connection issues #762

Open
mortenson opened this issue Dec 17, 2023 · 2 comments
Open
Assignees

Comments

@mortenson
Copy link

The problem I am facing
I'm trying to make my implementation of the HocuspocusProvider more resilient to websocket issues (ex: downtime in hocuspocus server, auth issues, network connectivity) and am not sure how to handle failure states gracefully.

The solution I would like
It would be nice if I could catch all failure states related to the initialization of the connection - specifically the opening of the websocket and the authentication steps. If anything fails, I'd like to fallback to a default value for prosemirror (or a default Y Doc, more generically) so that the user could at least edit content instead of looking at a blank textfield. Additionally, when a failure state is captured that indicates that reconnection is unlikely, it'd be nice to tell the provider to stop trying to reconnect.

I don't really need to handle cases where the connection drops while editing, since the user already has text they're working with. This is more about the initialization of the Y doc.

Alternatives I have considered
Feel like I've tried just about everything to detect the initial websocket connection failing due to connectivity issues, and nothing is working. Specifically provider.connect().catch() is not called, and you can't try/catch websocket connection failures. onAuthenticationFailed seems doable but if I implement that I still don't know how to tell the provider to stop trying to reconnect.

Additional context
I'm using y-prosemirror.

@mortenson
Copy link
Author

mortenson commented Dec 17, 2023

Totally other way of approaching this, which I think is better, is to use a default value for the Y.Doc but clear it before the websocket fetches data from the hocuspocus server, ex:

    const pdoc = defaultMarkdownParser.parse(textarea.value);
    const ydocument = prosemirrorToYDoc(pdoc);
    const yXmlFragment = ydocument.get('prosemirror', Y.XmlFragment) as Y.XmlFragment;  
    provider = new HocuspocusProvider({
      ...
     document: ydocument,
      onConnect: () => {
        yXmlFragment.delete(0, yXmlFragment.length);
      },
    });

But note that this example doesn't actually work because the yXmlFragment.delete is broadcast as a state change.

@mortenson
Copy link
Author

mortenson commented Dec 19, 2023

Here's my OK solution for now, basically only allow the initial connection to succeed once and fallback to non-yjs editing in that initial failure case:

  // Initial value based on "last saved draft", should probably initialize to current draft if one exists.
  const pdoc = defaultMarkdownParser.parse(textarea.value);
  let editable = false;
  const webSocketProvider = new HocuspocusProviderWebsocket({
      // Only try connecting once initially as to not delay editing.
      maxAttempts: 1,
      onConnect: () => {
        editable = true;
        // Once connected we're working with a semi-recent draft, no reason to fallback again.
        webSocketProvider.configuration.maxAttempts = 0;
      },
    });
    const provider = new HocuspocusProvider({
      websocketProvider: webSocketProvider,
    });
[...]
  const view = new EditorView(editorDiv, {
    state: EditorState.create({
      doc: pdoc || undefined,
      plugins: [<insert yjs plugins here>],
    }),
    editable: () => editable,
  });
[...]
    provider.connect().catch((e) => {
      console.error(e);
        editable = true;
        // Replace prosemirror view state with default value and fallback to non-yjs plugins.
        view.updateState(EditorState.create({
          schema: view.state.schema,
          doc: pdoc,
          plugins: [<insert non-yjs plugins here>],
        }));
    });

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

No branches or pull requests

2 participants