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

Event-based emit support? #49

Closed
TimKieu opened this issue Jun 21, 2024 · 5 comments
Closed

Event-based emit support? #49

TimKieu opened this issue Jun 21, 2024 · 5 comments
Labels
seen I've seen and read this issue and I'll try find some time soon to work on it.

Comments

@TimKieu
Copy link

TimKieu commented Jun 21, 2024

Current example uses polling-interval emit events.

if we have a stable connection, can we do emit fired by a given event?
For example, an order is updated from a client page, it updates server DB (REST/trpc/FormSubmit), then trigger SSE emit when update DB sucessfully or REST/trpc/FormSubmit workflow completed.

@razshare
Copy link
Owner

razshare commented Jun 21, 2024

Hello @TimKieu ,
yes you can do that, but you need to keep track of your users, for example using a session id.

A similar question has been asked in #34 and in #16

The last example uses an older api of the library, though it should still give you an idea how to do it.

The general idea is that you need to somehow identify a user, using an actual account or just an anonymous session id, it doesn't matter which way you do it. All that matters is that you have some string that identifies a unique client.

Then you create some global state that maps the user's id to an emitter function, the one you get when you call produce().

Something like this.

/** @type {Map<string,(eventName:string,data:string)=>import('sveltekit-sse').Unsafe<void,Error>>} */
const someGlobalMap = new Map()

export function POST({ request }) {
  return produce(
    function start({ emit }) {
      const sessionId = request.headers.get('session-id') ?? ''
      if (!sessionId) {
        return function stop() {
          console.error('Client session id not found.')
        }
      }
      // Save the emit function.
      // This will also indicate to you that the client is "online".
      someGlobalMap.set(sessionId, emit)
    },
    {
      stop() {
        // Client goes "offline", so remove the emit map.
        const sessionId = request.headers.get('session-id') ?? ''
        if (!sessionId) {
          return
        }
        someGlobalMap.delete(sessionId)
      },
    },
  )
}

This piece of code doesn't take into account that the same client may open you application from more than 1 browser tab, you'll need to add your own logic to implement that.

There are many ways to do that, which is beyond the scope of this library.
I'll give you some examples though, I'm sure you'll come up with something better

  1. Use a counter, your global map could also store a counter of how many connections the given client has opened. When that reaches 0 you remove the entry.
  2. Simply leave the emitter there. Whenever you emit things to a client that is not connected anymore, the emit() function returns an error, when that happens, you simply remove the entry from the map.
  3. Just ignore it. Very bad in production, but probably easier for experiments in dev mode.

Also you probably want to move someGlobalMap into its own module so that other modules can import it.


Let me know if this addresses your question.

@razshare razshare assigned razshare and unassigned razshare Jun 21, 2024
@razshare razshare added the seen I've seen and read this issue and I'll try find some time soon to work on it. label Jun 21, 2024
@TimKieu
Copy link
Author

TimKieu commented Jun 24, 2024

@razshare thanks much for your advice.
I will try on both local dev and production CloudFlare Pages & Workers to give you my feedback.
However, this may not be good for such distributed V8 system like CloudFlare/Vercel where the memory Map of emitters is not unique globally.

@razshare
Copy link
Owner

razshare commented Jun 24, 2024

However, this may not be good for such distributed V8 system like CloudFlare/Vercel where the memory Map of emitters is not unique globally.

Yes, obviously your map would have to be backed by some database, a broker, or something like that in cases of a distributed system, or maybe you don't even need specifically a map, some other data structure could fit better.

In any case, the idea remains the same: save your emitter so that you can retrieve it later.

@razshare
Copy link
Owner

razshare commented Jun 24, 2024

@TimKieu I've added a FAQ section with a few more details and a possible solution using Postgre, regarding this distributed systems matter and event forwarding.
The second point in that faq and the links in there will probably be of more interest to you.

@TimKieu
Copy link
Author

TimKieu commented Jun 26, 2024

@razshare Great! Thank you much indeed!
You made a great engine, useful for many use-cases and patterns.

@TimKieu TimKieu closed this as completed Jun 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
seen I've seen and read this issue and I'll try find some time soon to work on it.
Projects
None yet
Development

No branches or pull requests

2 participants