Bug #112211 Unexpected NPE and exception information during socket timeout
Submitted: 29 Aug 2023 12:37 Modified: 17 Nov 2023 19:52
Reporter: huanjie liu Email Updates:
Status: Verified Impact on me:
None 
Category:Connector / J Severity:S5 (Performance)
Version:8.0.33 OS:Any
Assigned to: CPU Architecture:Any
Tags: npe, socketTimeout

[29 Aug 2023 12:37] huanjie liu
Description:
When network fails down, unexpected exceptions will be thrown during the statement-execute phase:
Using PreparedStatement, throw a StatementIsClosedException, but it should be a CommunicationsException;
Using a Statement, throw an NPE, but it should be a CommunicationsException;
Under the above conditions, the thrown exception does not directly reflect the root cause of the problem, 
which can confuse those who see the exception information. The main reason is that unexpected NPEs occur within the connector/J during handling the socket-timeout;

How to repeat:
// copy below codes to any class
static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (Exception e) {
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (Exception t) {
                throw new RuntimeException(t);
            }
        }
    }

    public static final String MYSQL_URL_FORMAT = "jdbc:mysql://%s/%s?useSSL=false&characterEncoding=UTF-8";

    public void mysql8SocketTimeoutTest() throws SQLException {
        String statementType = "preparedStatement";
        int queryTimeout = 2;

        try(Connection connection = getConnection()) {
            if (statementType.equalsIgnoreCase("preparedStatement")) {
                try (PreparedStatement statement = connection.prepareStatement("select * from dalservicetable limit 10")) {
                    statement.setQueryTimeout(queryTimeout);
                    statement.execute();
                    // Omit for Processing of ResultSet
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                try(Statement statement = connection.createStatement()) {
                    statement.setQueryTimeout(queryTimeout);
                    statement.executeQuery("select * from dalservicetable limit 10");
                    // Omit for Processing of ResultSet
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(String.format(Constants.MYSQL_URL_FORMAT, "ip:port", "dbname"), "uid", "pwd");
    }

Suggested fix:
in src/main/core-impl/java/com/mysql/cj/AbstractQuery.java(228)
this.session.getCancelTimer().purge();

there should be a null check before purge the cancelTimer;

suggest:

if (this.session != null && this.session.getCancelTimer() != null)
      this.session.getCancelTimer().purge();
[17 Nov 2023 19:52] Filipe Silva
Hi Huanjie Liu,

Thank you for your interest in MySQL Connector/J and for taking the time to report this bug.

In the test case above you missed the socketTimeout setting in the connection string. Setting a value such as "socketTimeout=1000" does in fact cause the behavior you described, as such, I'm considering this report as verified.