apache / arrow

Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing
https://arrow.apache.org/
Apache License 2.0
14.19k stars 3.46k forks source link

[Java] Invocation of close method from ArrowFlightConnection throws a SQLException when using catalog as parameter #43305

Open scruz-denodo opened 1 month ago

scruz-denodo commented 1 month ago

Describe the bug, including details regarding any error messages, version, and platform.

Affected version flight-sql-jdbc-driver version 17.0.0

Description

With flight-sql-jdbc-driver version 17 the method org.apache.arrow.driver.jdbc.ArrowFlightConnection#close returns a SQLException when the connection has defined a value for catalog parameter.

I was testing this change and I am facing a problem when the java.sql.Connection#close method of the JDBC connection is called.

I am receiving the following error invoking the method.

Exception in thread "main" java.sql.SQLException: UNKNOWN: Uncaught exception in the SynchronizationContext. Re-thrown.
    at org.apache.calcite.avatica.Helper.createException(Helper.java:56)
    at org.apache.calcite.avatica.Helper.createException(Helper.java:41)
    at org.apache.arrow.driver.jdbc.ArrowFlightConnection.close(ArrowFlightConnection.java:187)
    at com.arrowflight.test.Main.test7(Main.java:249)
    at com.arrowflight.test.Main.main(Main.java:27)
Caused by: io.grpc.StatusRuntimeException: UNKNOWN: Uncaught exception in the SynchronizationContext. Re-thrown.
    at io.grpc.Status.asRuntimeException(Status.java:525)
    at io.grpc.internal.RetriableStream$1.uncaughtException(RetriableStream.java:75)
    at io.grpc.SynchronizationContext.drain(SynchronizationContext.java:96)
    at io.grpc.SynchronizationContext.execute(SynchronizationContext.java:126)
    at io.grpc.internal.RetriableStream.safeCloseMasterListener(RetriableStream.java:838)
    at io.grpc.internal.RetriableStream.cancel(RetriableStream.java:531)
    at io.grpc.internal.RetriableStream.start(RetriableStream.java:393)
    at io.grpc.internal.ClientCallImpl.startInternal(ClientCallImpl.java:285)
    at io.grpc.internal.ClientCallImpl.start(ClientCallImpl.java:184)
    at io.grpc.ForwardingClientCall.start(ForwardingClientCall.java:32)
    at org.apache.arrow.flight.grpc.ClientInterceptorAdapter$FlightClientCall.start(ClientInterceptorAdapter.java:142)
    at io.grpc.ForwardingClientCall.start(ForwardingClientCall.java:32)
    at io.grpc.stub.MetadataUtils$HeaderAttachingClientInterceptor$HeaderAttachingClientCall.start(MetadataUtils.java:75)
    at io.grpc.stub.ClientCalls.startCall(ClientCalls.java:335)
    at io.grpc.stub.ClientCalls.asyncUnaryRequestCall(ClientCalls.java:311)
    at io.grpc.stub.ClientCalls.blockingServerStreamingCall(ClientCalls.java:210)
    at org.apache.arrow.flight.impl.FlightServiceGrpc$FlightServiceBlockingStub.doAction(FlightServiceGrpc.java:874)
    at org.apache.arrow.flight.FlightClient.doAction(FlightClient.java:168)
    at org.apache.arrow.flight.FlightClient.closeSession(FlightClient.java:686)
    at org.apache.arrow.flight.sql.FlightSqlClient.closeSession(FlightSqlClient.java:998)
    at org.apache.arrow.driver.jdbc.client.ArrowFlightSqlClientHandler.close(ArrowFlightSqlClientHandler.java:224)
    at org.apache.arrow.util.AutoCloseables.close(AutoCloseables.java:97)
    at org.apache.arrow.util.AutoCloseables.close(AutoCloseables.java:75)
    at org.apache.arrow.driver.jdbc.ArrowFlightConnection.close(ArrowFlightConnection.java:181)
    ... 2 more
Caused by: java.util.concurrent.RejectedExecutionException: Task io.grpc.internal.SerializingExecutor@6ecdbab8 rejected from java.util.concurrent.ThreadPoolExecutor@3dd4a6fa[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 25]
    at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
    at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
    at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
    at io.grpc.internal.SerializingExecutor.schedule(SerializingExecutor.java:102)
    at io.grpc.internal.SerializingExecutor.execute(SerializingExecutor.java:95)
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.closedInternal(ClientCallImpl.java:736)
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.closed(ClientCallImpl.java:680)
    at io.grpc.internal.RetriableStream$4.run(RetriableStream.java:843)
    at io.grpc.SynchronizationContext.drain(SynchronizationContext.java:94)
    ... 23 more

I think the reason is the following. Checking this code:

https://github.com/apache/arrow/blob/6a2e19a852b367c72d7b12da4d104456491ed8b7/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightConnection.java#L174-L189

https://github.com/apache/arrow/blob/6a2e19a852b367c72d7b12da4d104456491ed8b7/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightConnection.java#L174-L189

I think that line line 181 from org.apache.arrow.driver.jdbc.ArrowFlightConnection#close should be remove for avoding the problem. The close is already done at 175.

Also, I would remove the is if: https://github.com/apache/arrow/blob/6a2e19a852b367c72d7b12da4d104456491ed8b7/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandler.java#L222-L225 why not invoking the closeSession when the catalog was not set?

It can be reproducible with this code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

public class CloseIssue {
    private static final String URL = "jdbc:arrow-flight-sql://localhost:9994?catalog=mydatabase&useEncryption=0";

    public static void main(String[] args) throws Exception {
        Class.forName("org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver");
        try (Connection conn = DriverManager.getConnection(URL, "admin", "admin")) {
            try (PreparedStatement pst = conn.prepareStatement("select 1");
                 ResultSet rs = pst.executeQuery()) {
                ResultSetMetaData rsm = rs.getMetaData();
                for (int i = 1; i <= rsm.getColumnCount(); i++) {
                    System.out.print(rsm.getColumnName(i) + " (" + rsm.getColumnType(i) + ") ");
                }
            }
        }
    }
}

This problems does not happen with prior versions, so it can be considered a regression.

Component(s)

Java

scruz-denodo commented 1 month ago

Issue was introduced on changes for this issue https://github.com/apache/arrow/issues/41947 at PR https://github.com/apache/arrow/pull/42035