Giter Club home page Giter Club logo

Comments (7)

yawaramin avatar yawaramin commented on May 23, 2024 1

Would the goal then be that the room_name is stored in the handler and that when the Topic.pull produces a messages, only the messages that are for room_name are pushed?

Right, in fact thinking about it you can probably have a route like: GET /ws/room_name and pass the room_name to the service and then on to the WS handler, like:

let socket = (~room_name, _) => {
  let%lwt subscription = Topic.subscribe(topic);
  let rec handler = (~room_name, pull, push) => {
    // handler will push only messages with { "room": room_name }
  };

  handler(~room_name) |> Response.of_websocket |> Lwt.return;
};

This certainly would be nice. It is nice that you can do some cleanup/detect when a client has been disconnected.

Agreed, in retrospect just having string option Lwt.t is a little too anemic. I'll make the change (but it will be a breaking change).

from re-web.

yawaramin avatar yawaramin commented on May 23, 2024

Hi Thomas, the ephemeral cache works by retaining the key-value pair as long as the key is in scope somewhere else, but drops it at some GC run after the key goes out of scope. So based on the observed behaviour it seems like the topic_name value is sometimes going out of scope?

Btw, I think we can rule out that cleanup line in find, because it's not called for find_opt. It was more an optimization to keep the hash table size trimmed for topics. But come to think of it, I should probably do the cleanup in find_opt as well.

from re-web.

tcoopman avatar tcoopman commented on May 23, 2024

Does the clean-up clean everything or just what's not in scope? It's not clear to me why that is exactly needed?

Ok, I'll see how I can find out when the string is not in scope anymore, but it's part of the websocket handler so I guessed it would stay in scope. I also don't know enough about ocaml internals/GC to have any good intuition on this, so it will be some searching around.

Actually I was wondering if it would be possible to get some kind of signal when the websocket drops/is closed in re-web, because then I could do the cleaning manually.

from re-web.

yawaramin avatar yawaramin commented on May 23, 2024

It would force a cleanup of any key-value pairs whose keys are not in scope. This is done automatically when the ephemeron-based hash table is resized, but resizing is non-deterministic, so I wanted a slightly stronger guarantee that cache memory wouldn't grow too much. See https://caml.inria.fr/pub/docs/manual-ocaml/libref/Ephemeron.K1.MakeSeeded.html

The topic_name value should stay in scope as long as the WS handler passes it in to itself recursively, but not otherwise because it would go out of scope as soon as one invocation of the handler function ended and its stack got cleared out.

Regarding getting an indication of when the socket gets closed, that's currently not possible...

from re-web.

tcoopman avatar tcoopman commented on May 23, 2024

The topic_name value should stay in scope as long as the WS handler passes it in to itself recursively, but not otherwise because it would go out of scope as soon as one invocation of the handler function ended and its stack got cleared out.

I've been able to make a smaller reproduction of the problem. The issues presents itself when the topic_name is part of the path, for example here: | (GET, ["ws", name]) => socket(cache, topic_name)`

I've created a gist with the relevant files here: https://gist.github.com/tcoopman/aa6727cef26fa8b734a150ae62c6fe74
If you use the index.js file and open a few tabs, you'll see you sometimes get CREATING A TOPIC as output.
If you change name to "a_fixed_name" here https://gist.github.com/tcoopman/aa6727cef26fa8b734a150ae62c6fe74#file-app-re-L9 this doesn't happen.

I've thought about passing the name to the handler as well as an extra argument, but that doesn't fix it.

Regarding getting an indication of when the socket gets closed, that's currently not possible...

Is that because of the architectural design, or just something that's not yet implemented?

from re-web.

yawaramin avatar yawaramin commented on May 23, 2024

Ah, I think I understand what you're trying to do–it's like a chat server where people can connect to chat rooms by a topic name, and it gets created automatically if it doesn't exist, and deleted automatically as soon as no one is connected to it any more.

This is difficult with the current design of the ephemeral cache because it works by using the physical (heap pointer) location of the cache key to decide whether the key-value pair should be kept or removed. So in your example, each time the socket service is invoked, it allocates a key by calling Context.Key(name), but doesn't actually store it anywhere. So each client that connects allocates a new key and looks up the topic in the ephemeral cache using that key.

Because the key is defined to use string value comparison in Context.Cache, it sometimes successfully looks up the topic, but sometimes fails and creates a new topic, because the topic's key-value pair has been cleared out by that time (because the allocated key went out of scope).

One way to get around this limitation is to have a single topic for all the 'chatrooms' and define a more structured message (maybe using a JSON encoding) to communicate with clients. The client-server WS communication can look something like:

Client: GET /ws
Server: (responds with WS)
C: { "tag": "join", "payload": { "room": "room_name" } }
S: (filters the messages it sends to the client so that it only sends messages with "room": "room_name")

To answer your last question, there's no signal when the WS gets closed. This is by design currently: see

| `Connection_close -> eof ()
. What happens is when the connection is closed, the handler is spun down without any warning. I've thought about how to actually give the WS handler a chance to gracefully exit. One method might be to change the return type of https://yawaramin.github.io/re-web/re-web/ReWeb/Response/index.html#type-pull from string option Lwt.t to (string, [> `Timeout | `Connection_close]) result Lwt.t, to convey what exactly happened. Would this be preferable, do you think?

from re-web.

tcoopman avatar tcoopman commented on May 23, 2024

Ok, thank you for explaining. It's clear to me know why this sometimes works and sometimes fails.

One way to get around this limitation is to have a single topic for all the 'chatrooms' and define a more structured message (maybe using a JSON encoding) to communicate with clients.

Would the goal then be that the room_name is stored in the handler and that when the Topic.pull produces a messages, only the messages that are for room_name are pushed?

Would this be preferable, do you think?

This certainly would be nice. It is nice that you can do some cleanup/detect when a client has been disconnected.

from re-web.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.