Infinite websockets created via Subscription?

Hi all,

I’m building an application using the RealWorld example app as a guide, and I hit some odd issue.

I have my pageSubscriptions method to declare subscriptions based on the page we have loaded, like:

pageSubscriptions : Flags -> Page -> Sub Msg
pageSubscriptions flags page =
    case page of
        Blank ->
            Sub.none

        Errored _ ->
            Sub.none

        NotFound ->
            Sub.none

        Login _ ->
            Sub.map ParseMessage (Login.subscriptions)

Where subscriptions does:

subscriptions = WebSocket.listen url

The problem is that, according to what I see in developer tools and the ‘events’ panel in Debug mode, subscriptions is called every minute or so, and that creates a new WebSocket connection. As a consequence, I end up with plenty of WebSocket connections open unless I refresh the page.

Has anyone seen this before? What’s the fix? I tried Google/SO/etc but couldn’t find any answer.

Cheers,
Pere

I think your Sub.map ParseMessage (Login.subscriptions) will create a subscription to the ParseMessage Msg in your application, resulting in Login.subscriptions being called each time a ParseMessage is produced.

I think what you want is

pageSubscriptions : Flags -> Page -> Sub Msg
pageSubscriptions flags page =
    case page of
        ....

        Login _ ->
            Login.subscriptions -- subscription created only once

Thanks for the answer! I have a follow up question:

I’m following the pattern from RealWorld example app for multiple pages, and it needs to map messages from pages to messages Main understands.

If what you mention is the issue, how can I map the messages from subscriptions when having multiple pages? What’s the alternative?

I assume you’re referring to the use of Html.map in the viewPage function. That takes the Msg values produced by the submodules and maps them onto a Msg defined in the Main module (e.g. it converts a Page.Login.Msg to a Main.Msg, which is necessary for it to be handled by the Main.update function).

All of this is very separate from the Sub msg values produced by your top level subscriptions function. The only time you would want to use Sub.map is if you wanted to “listen” for some other Msg value that gets produced, and then produce a different Msg as a result.

In the case of your websocket connection, that is not what you want to do. There, you want to create a single subscription that produces ParseMessage when a message comes in.

1 Like

So, thanks for the answer, but Sub.map wasn’t the issue. It seems the problem is the fact I call WebSocket.listen each time subscription is called, which happens regularly due to it being called after update as far as I can see.

How can I prevent the socket to be started each time? Is there any trick?

I believe the subscriptions are called on every iteration of the render loop, maybe you need to make sure that WebSocket.listen is only set up once?

1 Like

Yes, I guess so, I’m just surprised as all examples I’ve seen just call listen so I assumed it was doing some WS management behind the scenes… :frowning:

This code seems right to me. The websocket library should be keeping track of which connections are already open and not re-opening them.

I can think of two things that might be going wrong:

  1. The url passed to WebSocket.listen changes. In this case, the library would open a new connection for each url.

  2. The subscriptions method might be switching between returning WebSocket.listen and not. In this case, though, I’d expect a lot of opened connections but also a lot of closed ones.