Skip to content

High client usage when closing result set after huge query #877

Open
@David-Engel

Description

@David-Engel

This issue was brought up by a customer. I wanted to capture it in a GitHub issue in case others encounter it and need to know why the driver behaves the way it does.

Driver version

7.0.0

SQL Server version

SQL Server 2017

Client Operating System

Windows 10

JAVA/JVM version

JRE 10

Table schema

CREATE TABLE TestTable (TestColumn varchar(max));
create table testtable2 (testcolumn varchar(max))

declare @i int = 0
while @i < 10000
begin
insert into testtable2 (testcolumn) values
('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
select @i = @i + 1
end

set @i = 0
while @i < 500
begin
insert into testtable (testcolumn) select testcolumn from testtable2
select @i = @i + 1
end

Problem description

Execute a SQL query that returns a large result set, but only read a few rows before doing connection.rollback()

  1. Expected behavior:
    Rollback is executed, ResultSet is closed immediately (less than a second).
  2. Actual behavior:
    ResultSet.close() is causing all rows to be fetched from the server and taking a long time to close.

Reproduction code

    System.out.println("Start " + new java.util.Date().toString());
    String connectionString = "jdbc:sqlserver://<server>:1433;databaseName=<DBname>;user=<username>;password=<password>";
    try (Connection conn = DriverManager.getConnection(connectionString)) {
        conn.setAutoCommit(false);
        try (Statement statement = conn.createStatement()) {
            ResultSet rs = statement.executeQuery("select TestColumn from TestTable");
            try {
                while (rs.next()) {
                    System.out.println(rs.getString(1));
                    throw new SQLException("Testing...");
                }
            } finally {
                System.out.println("Closing rs " + new java.util.Date().toString());
                rs.close();
                System.out.println("Rs closed " + new java.util.Date().toString());
            }
        } catch (SQLException se) {
            se.printStackTrace();
            conn.rollback();
            System.out.println("Rollback complete " + new java.util.Date().toString());
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    System.out.println("Done " + new java.util.Date().toString());

Workaround

You can call statement.cancel() to abort processing of further results before calling rs.close(). This will have the effect of cancelling all result sets that might be following the first one being returned by the statement.

This behavior is by design and is not specific to the JDBC driver. All drivers that communicate with SQL Server must follow the TDS specification. A statement may return multiple result sets, whether it is multiple SELECT statements in a batch or a stored procedure which returns multiple result sets. Closing a result set tells the driver to move on to the next one and the only way to get to the next one is to fetch the stream of data until the next result set is encountered. There is no way to tell the server to skip ahead in the stream to the next result set. The server will not even begin producing/writing subsequent result sets to the stream until all previous rows are read.

Metadata

Metadata

Assignees

No one assigned

    Labels

    EnhancementAn enhancement to the driver. Lower priority than bugs.ExternalIssue is due to an external source we do not control.

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions