Phoenix 1.4rc3 Phoenix.Socket transport_pid nil on connect

One of the side effects of the changes in 1.4rc3 is that transport_pid is not set when the user_connect connect/2 is invoked. This makes it difficult to track the transport_pid separate from the channel pids.

I’m doing the below to get around this. It ends up creating a warning about init being defined twice. Is there any other way to achieve this pid tracking without a hacky solution?

defmodule PushExWeb.PushSocket do
  def init(state) do
    case Phoenix.Socket.__init__(state) do
      res = {:ok, {_, %Phoenix.Socket{} = socket}} ->
        PushEx.Instrumentation.Tracker.track_socket(socket)
        res

      res ->
        res
    end
  end

  use Phoenix.Socket

  channel "*", PushExWeb.PushChannel

  def connect(params, socket) do
    PushEx.Config.push_socket_connect_fn().(params, socket)
  end

  def id(socket) do
    PushEx.Config.push_socket_id_fn().(socket)
  end
end
2 Likes

@sb8244 please open up a bug report. If it worked before, it should continue to work now.

EDIT: I believe I am wrong. Please see @chrismccord’s reply below. :slight_smile:

1 Like

What is involved on your end with tracking the transport process? Are you monitoring it? Note, for instrumentation, we have the :phoenix_socket_connect instrumentation callback, which will allow you to hook into this without trying to override. The reason the transport_pid is nil on connect is we don’t spawn the transport until you have authorized the connection in UserSocket.connect/3, this way we don’t let folks upgrade to a spawned web socket process unless they are allowed to, preventing a DoS vector. Also note that Phoenix 1.4 includes the Phoenix.Socket behavior so you could write your own socket implementation (instead of use Phoenix.Socket and handle the tracking in your init. If you can share a little more about what you need I can say better which is the best option.

2 Likes

Chris, I am doing Process.link on the transport process in order to know when it comes offline. I checked out phoenix_socket_connect, but I don’t see the pid available:

{:stop, 232000,
 {:start,
  %{
    application: :phoenix,
    file: "/Users/stephenbussey/src/push_ex/test_frontend_socket/deps/phoenix/lib/phoenix/socket.ex",
    function: "__connect__/3",
    line: 470,
    module: Phoenix.Socket
  },
  %{
    connect_info: %{},
    endpoint: PushExWeb.Endpoint,
    log: :info,
    options: [
      serializer: [
        {Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"},
        {Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"}
      ],
      timeout: 60000,
      transport_log: false,
      compress: false
    ],
    params: %{"vsn" => "2.0.0"},
    transport: :websocket,
    user_socket: PushExWeb.PushSocket,
    vsn: "2.0.0"
  }}}

It does seem like creating my own Phoenix.Socket callbacks is the right call here.

Agree your own behavior is the way to go

1 Like

We had somewhat similar problem. We want to record how many sockets are online at any given time. In channels we can increase a metric in join/3 and decrease the metric in terminate/2. Socket however doesn’t have terminate callback. So currently we also created our own socket implementation where we were able to use init to increase and terminate to decrease the counts.

@chrismccord What do you think about adding terminate callback to Socket or what do you think about exposing a new telemetry metric phoenix.socket_disconnected (there already is phoenix.socket_connected)?

Not sure if the topic author was tracking transport_pid for the same reason though.

It is hard to do socket_disconnected because a process can terminate due to links and exit signals, which means the metric wouldn’t be invoked.

However, what you can do is to send a message to a watcher process on the socket_connected event. The watcher process then monitors the socket and emits the socket_disconnected, without a need for a custom socket implementation.

2 Likes

@josevalim @chrismccord just want to plus one there’s a real business need in here – exposing socket connection counts in an easy way. I’ve got a service which I may get a shot moving into production and the first two things the operational side wanted onhand sourced from inside the app:

  • websocket connection count instrumentation
  • channel usage instrumentation

And so I’m here in these kinds of posts. I can probably roll my own if I have to but this feels like a recurring situation for something going prod in a company.