Azolo / websockex

An Elixir Websocket Client
MIT License
520 stars 100 forks source link

No reconnection (and lack of debugger) #129

Open centuriononon opened 7 months ago

centuriononon commented 7 months ago

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

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

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

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"?

roylez commented 6 months ago

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