Skip to content

MySQL 8 Connection Timeout (4031) Incorrectly Recognized as Valid by test_connection #195

@kelefat

Description

@kelefat

In MySQL 8, when a connection exceeds the wait_timeout period, the server sends an error packet with code ‌4031‌. However, gent_tcp:recv successfully receives this packet while the connection is actually closed. This causes test_connection to mistakenly treat it as a valid connection.

The error packet structure is:

{error_packet, 0, 4031, <<"HY000">>, "The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior."}
test_connection(Conn, StayLocked) ->
  case catch emysql_tcp:send_and_recv_packet(Conn#emysql_connection.socket, <<?COM_PING>>, 0) of
    {'EXIT', _} ->
      case reset_connection(emysql_conn_mgr:pools(), Conn, StayLocked) of
        NewConn when is_record(NewConn, emysql_connection) ->
          NewConn;
        {error, FailedReset} ->
          exit({connection_down, {and_conn_reset_failed, FailedReset}})
      end;
    _ ->
       NewConn = Conn#emysql_connection{last_test_time = now_seconds()},
       case StayLocked of
         pass -> emysql_conn_mgr:replace_connection_as_available(Conn, NewConn);
         keep -> emysql_conn_mgr:replace_connection_as_locked(Conn, NewConn)
       end,
       NewConn
  end.

When sending a ping package, should we only consider the connection healthy if an OK packet is received? Would this be a better approach?

test_connection(Conn, StayLocked) ->
  case catch emysql_tcp:send_and_recv_packet(Conn#emysql_connection.socket, <<?COM_PING>>, 0) of
    #ok_packet{} ->
       NewConn = Conn#emysql_connection{last_test_time = now_seconds()},
       case StayLocked of
         pass -> emysql_conn_mgr:replace_connection_as_available(Conn, NewConn);
         keep -> emysql_conn_mgr:replace_connection_as_locked(Conn, NewConn)
       end,
       NewConn
    _ ->
      case reset_connection(emysql_conn_mgr:pools(), Conn, StayLocked) of
        NewConn when is_record(NewConn, emysql_connection) ->
          NewConn;
        {error, FailedReset} ->
          exit({connection_down, {and_conn_reset_failed, FailedReset}})
      end;
  end.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions