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

Mercure preventing graceful reload of Octane / EventSource best-practice #1177

Open
jackwh opened this issue Nov 17, 2024 · 3 comments
Open
Labels
bug Something isn't working

Comments

@jackwh
Copy link

jackwh commented Nov 17, 2024

Hi there! I just upgraded my Laravel Octane app to use FrankenPHP 1.3.1. Since the update I've been tinkering with Mercure, this is my first time trying it but it's looking pretty great so far 🙌

I have a couple of questions which I couldn't figure out from the docs. I'm not sure if I'm doing something wrong, or this is behaving as expected.

  1. Mercure preventing graceful reload of Octane?
  • Whenever I change backend Laravel code, Octane detects the change and reloads the FrankenPHP worker (as expected).
  • But since I've updated my frontend code to establish a connection to the Mercure EventSource, reloads appear to hang/freeze, until I refresh the webpage connected to the EventSource in my browser.
  • The moment I refresh, Octane carries on like nothing happened.
  • I'm not sure if this is an issue with Octane or my setup, or if it's expected. If it's expected, how does this work during deployment reloads?
  1. Subscribing to multiple topics independently (unrelated to Octane, but I couldn't find a clear answer...)
  • My app's frontend is built with React, and I want to subscribe to multiple topics depending which components are visible on the page.
  • Because these components are loaded dynamically, I'm currently just setting up a new EventSource for each component which needs to listen to a topic.
  • When a component unmounts, useEffect calls eventSource.close() to tidy up the connection
  • This approach seems quite inefficient... If 5 components need to subscribe, 5 separate network requests appear in devtools. I figure this will add significant overhead if running at scale.

I assume I need to change my setup so there's some global/shared EventSource. Any components mounting/unmounting will need to call eventSource.close() on the global source, update the global list of topics, then re-subscribe the global source to the new list.

This... also seems inefficient? But I might be going about it completely wrong. Never mind the fact React makes shared state as hard as possible 🙃

Thanks for any pointers, it's much appreciated :)

Build Type

Official static build

Worker Mode

Yes

Operating System

macOS

CPU Architecture

Apple Silicon

PHP configuration

N/A

Relevant log output

No response

@jackwh jackwh added the bug Something isn't working label Nov 17, 2024
@withinboredom
Copy link
Collaborator

For number 2, I would suggest creating an eventsource provider (or using an existing library -- it's javascript, there's bound to be at least one of dubious quality or better 😆) and reference counting for topics. So, a component subscribing to a topic will increment the topic reference count, and when it hits zero for long enough (2-3s? or whatever makes sense for your user behavior) then close the eventsource. Providers are great for shared state.

For number 1, it might be better to ask it on the mecure github repository?

@jackwh
Copy link
Author

jackwh commented Nov 17, 2024

Thanks @withinboredom! I found a couple of libraries but ended up writing my own, no doubt it's far more dubious than any of the alternatives 🤣 Here's what I came up with in case anyone else finds it useful... https://gist.github.com/jackwh/cdb22640ff962bc8fa86cf79017ff8cd

I ended up using a global instance on the window as I needed this to be React-agnostic so the non-React parts of my frontend can interact with it too. Instead of reference counting I used a Map with hashed subscription keys. This was mostly due to the unusual requirements of my app, but hey, it works well for my use case.

Re no. 1, I think this is Octane-specific but I'll check the Mercure repo too. Cheers!

@dunglas
Copy link
Owner

dunglas commented Nov 18, 2024

For 1, it should be possible to improve the situation by using the new watcher mode, it prevents closing the SSE connection because only PHP workers restart on file change, not the whole server. I'm on it.

For 2, you may be interested in @soyuka's https://edge-side-api.rocks/mercure.

If you want to do it manually, we recommend using a single global EventSource instance and closing the connection then reconnecting when a topic is added/removed. Passing the ID of the last received event in the lastEventID query parameter allows us to be sure not to miss any message during reconnection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants