Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unnecessary use of v3 Parse/Bind/Execute and named portal sending BEGIN for COPY #1638

Closed
ringerc opened this issue Dec 6, 2019 · 7 comments · Fixed by #1639
Closed

Unnecessary use of v3 Parse/Bind/Execute and named portal sending BEGIN for COPY #1638

ringerc opened this issue Dec 6, 2019 · 7 comments · Fixed by #1639

Comments

@ringerc
Copy link
Member

ringerc commented Dec 6, 2019

Bug.

PgJDBC uses a v3 protocol Parse/Bind/Execute and may use a named portal ("prepared statement" at the protocol level) when autocommit is off, there is no in-progress transaction and a COPY operation is started via the CopyManager.

This is inefficient but mostly harmless. However, it upsets setups that use PgBouncer in transaction pooling mode with round-robin and/or a server lifetime limit, since the named portal for the BEGIN may vanish out from under PgJDBC. If this happens, the trace log should contain something like:

org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse
FINEST:  <=BE ErrorMessage(ERROR: prepared statement "S_1" does not exist

This will be the case even if prepareThreshold=0.

There's no good reason to use the extended query protocol for BEGIN so I'll submit a patch to force simple query mode.

@ringerc
Copy link
Member Author

ringerc commented Dec 6, 2019

Workarounds:

  • Set autocommit on before doing COPY; or
  • Send an explicit BEGIN statement on the connection before beginning the COPY

@ringerc
Copy link
Member Author

ringerc commented Dec 6, 2019

It looks like there's an opportunity to detect if the connection has been put in read-only state and send the read-only begin instead. I haven't made that change at the same time.

@ringerc
Copy link
Member Author

ringerc commented Dec 6, 2019

Will send a PR, but here's the patch I expect will do the trick

diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
index c944ca03..20ce1762 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
@@ -630,8 +630,12 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       };
 
       try {
-        sendOneQuery(beginTransactionQuery, SimpleQuery.NO_PARAMETERS, 0, 0,
-            QueryExecutor.QUERY_NO_METADATA);
+        /* Send BEGIN with simple protocol preferred */
+        int beginFlags = QueryExecutor.QUERY_NO_METADATA
+                         | QueryExecutor.QUERY_ONESHOT
+                         | QueryExecutor.QUERY_EXECUTE_AS_SIMPLE;
+        beginFlags = updateQueryMode(beginFlags);
+        sendOneQuery(beginTransactionQuery, SimpleQuery.NO_PARAMETERS, 0, 0, beginFlags);
         sendSync();
         processResults(handler, 0);
         estimatedReceiveBufferBytes = 0;

@vlsi
Copy link
Member

vlsi commented Dec 6, 2019

There's no good reason to use the extended query protocol for BEGIN

Is it baked with the measurements?

ringerc added a commit to ringerc/pgjdbc that referenced this issue Dec 6, 2019
When autocommit is off and the first query in a transaction is a COPY, we were
sending a BEGIN using the extended query protocol. It was being permitted to
use named portals as well, ignoring prepareThreshold.

Fix by forcing simple query mode and marking the BEGIN as oneshot.

Fixes pgjdbc#1638
@ringerc
Copy link
Member Author

ringerc commented Dec 6, 2019

Created as #1639

Backed by measurements? No, but we don't use extended query protocol for BEGIN anywhere else unless forced, and this was clearly an oversight. And 3 protocol messages is more work than 1. I know colleagues have done work that's found that the extended query protocol adds non-trivial overhead for simple statements, but I cannot presently point to specific benchmarks etc.

Look at sendQueryPreamble. This patch makes the COPY setup path more consistent with the BEGIN we send there.

@ringerc
Copy link
Member Author

ringerc commented Dec 6, 2019

Note that this is very much a bug fix - we use extended query protocol even if the query mode is set to simple. We may use named portals (prepared statements) even if prepareThreshold=0. We're not respecting the configuration here.

It just so happens that it's a pretty harmless bug unless you're using a weird pooler configuration.

davecramer pushed a commit that referenced this issue Dec 6, 2019
When autocommit is off and the first query in a transaction is a COPY, we were
sending a BEGIN using the extended query protocol. It was being permitted to
use named portals as well, ignoring prepareThreshold.

Fix by forcing simple query mode and marking the BEGIN as oneshot.

Fixes #1638
@ringerc
Copy link
Member Author

ringerc commented Dec 7, 2019

Thanks @davecramer :) much appreciated. Tried to supply a decent report to make it easy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants