yandex / odyssey

Scalable PostgreSQL connection pooler
BSD 3-Clause "New" or "Revised" License
3.18k stars 159 forks source link

Client error when pool_reserve_prepared_statement is true #564

Open dimitriusx opened 8 months ago

dimitriusx commented 8 months ago

We use odyssey with enabled pool_reserve_prepared_statement. After this commit our liquidbase client can't apply migrations to a Postgres (15) with error:

Invocation of init method failed; nested exception is java.lang.IllegalStateException: Received resultset tuples, but no field structure for them

PS. We use actual version of JDBC driver (postgresql-42.7.1.jar)

x4m commented 8 months ago

@NikitaUnisikhin please take a look. There's a superposition of bugs and features in this COPY stuff...

NikitaUnisikhin commented 8 months ago

@dimitriusx hi, can I see the query?

PashaKirillov commented 7 months ago

I am facing the same issue on Postges version 14. Error appears for any query with autoCommit set to false, here is my app to reproduce:

    val ds = new PGSimpleDataSource

    ds.setUrl("jdbc:postgresql://odyssey:6432/testdb?ApplicationName=myTestApp")
    ds.setUser("test")
    ds.setPassword("password")

    val connection = ds.getConnection
    connection.setAutoCommit(false)

    val stmt = connection.createStatement()
    val rs = stmt.executeQuery("select version()")

    if (rs.next) logger info rs.getString(1)

    connection.close()

trace:

TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Parse(stmt=null,query="BEGIN",oids={})
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Bind(stmt=null,portal=null)
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Execute(portal=null,limit=0)
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Parse(stmt=null,query="select version()",oids={})
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Bind(stmt=null,portal=null)
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Describe(portal=null)
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Execute(portal=null,limit=0)
TRACE org.postgresql.core.v3.QueryExecutorImpl -  FE=> Sync
TRACE org.postgresql.core.v3.QueryExecutorImpl -  <=BE ParseComplete [null]
TRACE org.postgresql.core.v3.QueryExecutorImpl -  <=BE ParseComplete [null]
TRACE org.postgresql.core.v3.QueryExecutorImpl -  <=BE BindComplete [unnamed]
TRACE org.postgresql.core.v3.QueryExecutorImpl -  <=BE BindComplete [unnamed]
TRACE org.postgresql.core.v3.QueryExecutorImpl -  <=BE DataRow(len=104)
TRACE org.postgresql.core.v3.QueryExecutorImpl -  <=BE CommandStatus(SELECT 1)
Exception in thread "main" java.lang.IllegalStateException: Received resultset tuples, but no field structure for them
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2336)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:368)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:498)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:415)
    at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:335)
    at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:321)
    at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:297)
    at org.postgresql.jdbc.PgStatement.executeQuery(PgStatement.java:246)

Very strange sequence of lines in <=BE section and it is not a logging issue, tcpdump shows 1, 1, 2, 2, D, C if i got it correctly.

postgres log:

statement: SET DateStyle=E'ISO';
execute 29cc8786: SET extra_float_digits = 3
statement: SET DateStyle=E'ISO';
execute 942206c3: SET application_name = 'myTestApp'
statement: SET DateStyle=E'ISO';
execute ae94974c: select version()
execute fetch from ae94974c: select version()
statement: SET DateStyle=E'ISO';

odyssey suppress BEGIN statement although application send it.

database default {
        user default {
                authentication "md5"
                auth_query "SELECT usename, passwd FROM pg_shadow WHERE usename=$1"
                auth_query_user "odyssey_auth_query"
                auth_query_db "postgres"
                storage "pg"
                pool "transaction"
                pool_size 3000
                pool_timeout 0
                pool_ttl 60
                pool_discard no
                pool_smart_discard no
                pool_reserve_prepared_statement yes
                pool_cancel yes
                pool_rollback yes
                client_fwd_error yes
                application_name_add_host no
                server_lifetime 3600
                quantiles "0.99,0.95,0.5"
                log_query yes
                log_debug yes
        }
}

my workaround is to set defaultRowFetchSize to any non zero value in jdbc url

reshke commented 7 months ago

seems that issue is connected with https://github.com/yandex/odyssey/commit/806aebdd36d6cdbe8f50811548ba5472125f2487

I think we need to add some simple driver test againts odyssey, like in SPQR https://github.com/pg-sharding/jdbc-spqr