Uploaded image for project: 'MariaDB Connector/J'
  1. MariaDB Connector/J
  2. CONJ-407

Connection.setTransactionIsolation lost on MaxAllowedPacketException

Details

    • Bug
    • Status: Closed (View Workflow)
    • Major
    • Resolution: Fixed
    • 1.5.6
    • 1.5.7
    • Other
    • None

    Description

      Transaction isolation level set via Connection#setTransactionIsolation is lost,
      when statement are executed on such a connection that trigger an MaxAllowedPacketException.

      Although the connection is still usable, it silently switches back to the default isolation behaviour,

      Testcase:

      public class IsolationLevelLostOnReconnect {
       
        @Test
        public void isolationLevelResets() throws SQLException {
          try (Connection c = DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "test", "test")) {
            long max = maxPacket(c);
            if (max > Integer.MAX_VALUE - 10) {
              fail("max_allowed_packet to high for this test");
            }
            c.prepareStatement("create table if not exists foo (x longblob)").execute();
            c.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
            assertThat(level(c), is("READ-UNCOMMITTED"));
            try (PreparedStatement st = c.prepareStatement("insert into foo (?)")) {
              st.setBytes(1, data((int) (max + 10)));
              st.execute();
              fail();
            } catch (SQLException e) {
              assertThat(e.getMessage(), containsString("max_allowed_packet"));
              // we still have a working connection
              assertThat(maxPacket(c), is(max));
              // but our isolation level changed:
              assertThat(level(c), is("READ-UNCOMMITTED"));
            }
          }
        }
       
        private String level(Connection c) throws SQLException {
          try (ResultSet rs = c.prepareStatement("select @@tx_isolation").executeQuery()) {
            rs.next();
            return rs.getString(1);
          }
        }
       
        private long maxPacket(Connection c) throws SQLException {
          try (ResultSet rs = c.prepareStatement("select @@max_allowed_packet").executeQuery()) {
            rs.next();
            return rs.getLong(1);
          }
        }
       
        private byte[] data(int size) {
          byte[] data = new byte[size];
          Arrays.fill(data, (byte) 'a');
          return data;
        }
      }
      

      Javadoc on Connection#setTransactionIsolation reads (excerpt):

           * Attempts to change the transaction isolation level for this
           * <code>Connection</code> object to the one given.
      

      Clearly, this does not hold in this situation.

      Attachments

        Activity

          pretzer Matthias Pretzer created issue -
          pretzer Matthias Pretzer made changes -
          Field Original Value New Value
          Description Transaction isolation level set via {{Connection#setTransactionIsolation}} is lost,
          when statement are executed on such a connection that trigger an MaxAllowedPacketException.

          Although the connection is still usable, it silently switches back to the default isolation behaviour,

          Testcase:

          {code}
          public class IsolationLevelLostOnReconnect {

            @Test
            public void isolationLevelResets() throws SQLException {
              try (Connection c = DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "test", "test")) {
                long max = maxPacket(c);
                if (max > Integer.MAX_VALUE - 10) {
                  fail("max_allowed_packet to high for this test");
                }
                c.prepareStatement("create table if not exists foo (x longblob)").execute();
                c.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
                assertThat(level(c), is("READ-UNCOMMITTED"));
                try (PreparedStatement st = c.prepareStatement("insert into foo (?)")) {
                  st.setBytes(1, data((int) (max + 10)));
                  st.execute();
                  fail();
                } catch (SQLException e) {
                  assertThat(e.getMessage(), containsString("max_allowed_packet"));
                  // we still have a working connection
                  assertThat(maxPacket(c), is(max));
                  // but our isolation level changed:
                  assertThat(level(c), is("READ-UNCOMMITTED"));
                }
              }
            }

            private String level(Connection c) throws SQLException {
              try (ResultSet rs = c.prepareStatement("select @@tx_isolation").executeQuery()) {
                rs.next();
                return rs.getString(1);
              }
            }

            private long maxPacket(Connection c) throws SQLException {
              try (ResultSet rs = c.prepareStatement("select @@max_allowed_packet").executeQuery()) {
                rs.next();
                return rs.getLong(1);
              }
            }

            private byte[] data(int size) {
              byte[] data = new byte[size];
              Arrays.fill(data, (byte) 'a');
              return data;
            }
          }
          {code}

          Javadoc on {{Connection#setTransactionIsolation}} reads (excerpt):
          {code}
               * Attempts to change the transaction isolation level for this
               * <code>Connection</code> object to the one given.
          {code}

          Clearly, this does not hold in this situation.
          Transaction isolation level set via {{Connection#setTransactionIsolation}} is lost,
          when statement are executed on such a connection that trigger an MaxAllowedPacketException.

          Although the connection is still usable, it silently switches back to the default isolation behaviour,

          Testcase:

          {code:java}
          public class IsolationLevelLostOnReconnect {

            @Test
            public void isolationLevelResets() throws SQLException {
              try (Connection c = DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "test", "test")) {
                long max = maxPacket(c);
                if (max > Integer.MAX_VALUE - 10) {
                  fail("max_allowed_packet to high for this test");
                }
                c.prepareStatement("create table if not exists foo (x longblob)").execute();
                c.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
                assertThat(level(c), is("READ-UNCOMMITTED"));
                try (PreparedStatement st = c.prepareStatement("insert into foo (?)")) {
                  st.setBytes(1, data((int) (max + 10)));
                  st.execute();
                  fail();
                } catch (SQLException e) {
                  assertThat(e.getMessage(), containsString("max_allowed_packet"));
                  // we still have a working connection
                  assertThat(maxPacket(c), is(max));
                  // but our isolation level changed:
                  assertThat(level(c), is("READ-UNCOMMITTED"));
                }
              }
            }

            private String level(Connection c) throws SQLException {
              try (ResultSet rs = c.prepareStatement("select @@tx_isolation").executeQuery()) {
                rs.next();
                return rs.getString(1);
              }
            }

            private long maxPacket(Connection c) throws SQLException {
              try (ResultSet rs = c.prepareStatement("select @@max_allowed_packet").executeQuery()) {
                rs.next();
                return rs.getLong(1);
              }
            }

            private byte[] data(int size) {
              byte[] data = new byte[size];
              Arrays.fill(data, (byte) 'a');
              return data;
            }
          }
          {code}

          Javadoc on {{Connection#setTransactionIsolation}} reads (excerpt):
          {code:java}
               * Attempts to change the transaction isolation level for this
               * <code>Connection</code> object to the one given.
          {code}

          Clearly, this does not hold in this situation.
          pretzer Matthias Pretzer made changes -
          Attachment IsolationLevelLostOnReconnect.java [ 43201 ]

          The following patch fixes the test case by ensuring non-default isolation levels are applied again upon reconnecting.

          diff --git a/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java b/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java
          index 95fd170..88c6445 100644
          --- a/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java
          +++ b/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java
          @@ -451,6 +451,9 @@ public abstract class AbstractConnectProtocol implements Protocol {
                       sessionOption += "," + options.sessionVariables;
                   }
                   executeQuery("set session " + sessionOption);
          +        if (getTransactionIsolationLevel() != 0) {
          +          setTransactionIsolation(getTransactionIsolationLevel());
          +        }
               }
           
               private void handleConnectionPhases() throws QueryException {
          

          I did run mvn test locally and there were no differences in the outcome (severel tests failed and several skipped, I assume because of my environment).

          pretzer Matthias Pretzer added a comment - The following patch fixes the test case by ensuring non-default isolation levels are applied again upon reconnecting. diff --git a/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java b/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java index 95fd170..88c6445 100644 --- a/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java +++ b/src/main/java/org/mariadb/jdbc/internal/protocol/AbstractConnectProtocol.java @@ -451,6 +451,9 @@ public abstract class AbstractConnectProtocol implements Protocol { sessionOption += "," + options.sessionVariables; } executeQuery("set session " + sessionOption); + if (getTransactionIsolationLevel() != 0) { + setTransactionIsolation(getTransactionIsolationLevel()); + } }   private void handleConnectionPhases() throws QueryException { I did run mvn test locally and there were no differences in the outcome (severel tests failed and several skipped, I assume because of my environment).
          diego dupin Diego Dupin added a comment -

          right, failover will have the same issue.

          diego dupin Diego Dupin added a comment - right, failover will have the same issue.
          diego dupin Diego Dupin made changes -
          Fix Version/s 1.6.0 [ 22119 ]
          diego dupin Diego Dupin made changes -
          Fix Version/s 1.5.7 [ 22500 ]
          Fix Version/s 1.6.0 [ 22119 ]
          diego dupin Diego Dupin made changes -
          Status Open [ 1 ] In Progress [ 3 ]
          diego dupin Diego Dupin made changes -
          Resolution Fixed [ 1 ]
          Status In Progress [ 3 ] Closed [ 6 ]
          serg Sergei Golubchik made changes -
          Workflow MariaDB v3 [ 79115 ] MariaDB v4 [ 134941 ]

          People

            diego dupin Diego Dupin
            pretzer Matthias Pretzer
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.