websockex icon indicating copy to clipboard operation
websockex copied to clipboard

No reconnection (and lack of debugger)

Open centuriononon opened this issue 1 year ago • 1 comments

Conditions

OS: Arch Linux x86_64 Kernel: 6.8.1-arch1-1 Erlang: 26.0 Elixir: 1.16.0-otp-26

Dependencies: Jason: 0.4.3 WebSockex: 1.4.1

Description

The problem is that when disconnecting the ethernet device, the WebSockex server does not always reconnect.

It reconnects only if the connection is lost for no more than 5 seconds. If more time has passed, it does not reconnect anymore and the debugger does not register anything useful.

Flow I expected

  1. Check ethernet status: eno1 connected
  2. Connect WebSockex: start my BasicSocket, establish connection, and subscribe on events (exchange stuff)
  3. Disconnect ethernet, socket lost connection and does not receive events
  4. Wait for a while...
  5. Connect ethernet, socket establishes connection and continue receive events

Flow 1: Fast reconnection, as I expected

  1. Check ethernet status...
  2. Connect WebSockex...
  3. Disconnect ethernet...
  4. Wait for at most 5 seconds
  5. Connect ethernet, socket maybe establishes connection and receive events anymore!

Flow 2: Long reconnection, as I did not expected

  1. Check ethernet status...
  2. Connect WebSockex...
  3. Disconnect ethernet...
  4. Wait for at least 5 seconds
  5. Connect ethernet, socket maybe establishes connection and does not receive events anymore!

Initial setup 15:01:47 (eno1 connected, socket receives events): image

  • I started new WebSockex server, and subscribed on events with a special subscription message -- socket established connection, socket began receiving events.

Disconnection 15:02:10 (eno1 disconnected, socket does not receive events): image

  • I disconnected eno1 (my ethernet), and it stopped socket.

Reconnection 15:07:12 (eno1 connected, socket still does not receive events) image

  • I reconnected eno1 again. But still no events. And even no logs. It even does not try to establish connection again.

BasicSocket:

defmodule Experiments.BasicSocket do
  use WebSockex

  require Logger

  alias :zlib, as: Zlib
  alias Experiments.BasicSocket.Helpers

  @origin "wss://open-api-swap.bingx.com/swap-market"

  def start_link(state, options \\ []) do
    WebSockex.start_link(@origin, __MODULE__, state, options)
  end

  def send(pid, message) do
    WebSockex.cast(pid, {:"$send", message})
  end

  @impl true
  def handle_connect(_conn, state) do
    channel = Helpers.sub_message()
    __MODULE__.send(self(), channel)

    {:ok, state}
  end

  @impl true
  def handle_disconnect(_details, state) do
    {:reconnect, state}
  end

  @impl true
  def handle_frame({:binary, frame}, state) do
    case Zlib.gunzip(frame) do
      "Ping" ->
        {:reply, {:text, "Pong"}, state}

      _ ->
        Logger.info("*INFO* event received")
        {:ok, state}
    end
  end

  @impl true
  def handle_cast({:"$send", message}, state) do
    {:reply, {:text, message}, state}
  end
end

Main question

Why WebSockex reconnects for a short disconnection periods, and does not reconnect for long periods in this case?

Side question

Why is the debugger so laggy in iex sessions?

Are there other options to debug my case? How can I get additional information about what happens under the "hood"?

centuriononon avatar Apr 04 '24 18:04 centuriononon

Thanks for pointing me to the right direction as this has been troubling me for some time.

A workaround is to send a ping frame every 30 seconds.

  def handle_pong(_, %{ connected: false }=state), do: {:ok, state}
  def handle_pong(_, %{ connected: true }=state),  do: {:ok, %{ state| pong_received: true }}

  def handle_connect(_conn, _state) do
    Process.send_after(self(), :ping_timeout, @ping_timeout)
    { :ok, %__MODULE__{ pong_received: true, connected: true } }
  end

  def handle_info(:ping_timeout, state) do
    case state do
      %{ pong_received: true } ->
        Process.send_after(self(), :ping_timeout, @ping_timeout)
        {:reply, {:ping, ""}, %{ state| pong_received: false }}
      %{ pong_received: false } ->
        {:close, state}
    end
  end

roylez avatar May 02 '24 05:05 roylez