Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lvyOA-00068V-Vt for pgsql-hackers@arkaria.postgresql.org; Wed, 23 Jun 2021 08:37:42 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1lvyO8-00046D-6U for pgsql-hackers@arkaria.postgresql.org; Wed, 23 Jun 2021 08:37:36 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lvyO6-000464-5J for pgsql-hackers@lists.postgresql.org; Wed, 23 Jun 2021 08:37:35 +0000 Received: from codesynthesis.com ([188.40.148.39]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lvyO3-0005L5-94 for pgsql-hackers@lists.postgresql.org; Wed, 23 Jun 2021 08:37:33 +0000 Received: from brak.codesynthesis.com (unknown [105.225.47.95]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by codesynthesis.com (Postfix) with ESMTPSA id 6388F5F301; Wed, 23 Jun 2021 08:37:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codesynthesis.com; s=mail1; t=1624437448; bh=25SspNDfHghF96Vz8mebNEKExDOL+6+IJL8WogoyO50=; h=Date:From:To:Subject:Message-ID:MIME-Version:From; b=hm0hbRWvbj6KBsiajtL6ikhpSWQdJhfFN/a+L1P/XtHV0WsnLFwOmSvZ4YsKAE4te OzLAGZPAEg/qVHOH2qKT9z+B++LYfaqGf8vnSB7KBmjmTaIhYjV82hNfYaNAxBD81u YyQ6CZuwgh6X1GlmA0vHd6O8rQHh6iqmpFGtjIHmE6XJK0+XyFWheyRGGIcWj/OQNE YHpJ4CwSrNd4uuow2vxEoYh0X5wnlDh72YLMDmBkJ3I77m+8PZNLcVQl5Mjdr9HCNR oIRkvws7WAb6l49Y3rtjWdgdfs3+WwfWQtgf52bjP7LML50I6ZC6rJkyhunAvqU0o5 kQ/kwqR8FqWVg== Received: by brak.codesynthesis.com (Postfix, from userid 1000) id 3DDF21A802C0; Wed, 23 Jun 2021 10:37:22 +0200 (SAST) Date: Wed, 23 Jun 2021 10:37:22 +0200 From: Boris Kolpackov To: Alvaro Herrera Cc: pgsql-hackers@lists.postgresql.org Subject: Re: Pipeline mode and PQpipelineSync() Message-ID: References: <202106222214.ptjfmstb23mu@alvherre.pgsql> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <202106222214.ptjfmstb23mu@alvherre.pgsql> Organization: Code Synthesis User-Agent: Mutt/1.5.24 (2015-08-30) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Alvaro Herrera writes: > > I think always requiring PQpipelineSync() is fine since it also serves > > as an error recovery boundary. But the fact that the server waits until > > the sync message to start executing the pipeline is surprising. To me > > this seems to go contrary to the idea of a "pipeline". > > But does that actually happen? There's a very easy test we can do by > sending queries that sleep. If my libpq program sends a "SELECT > pg_sleep(2)", then PQflush(), then sleep in the client program two more > seconds without sending the sync; and *then* send the sync, I find that > the program takes 2 seconds, not four. This shows that both client and > server slept in parallel, even though I didn't send the Sync until after > the client was done sleeping. Thanks for looking into it. My experiments were with INSERT and I now was able to try things with larger pipelines. I can now see the server starts sending results after ~400 statements. So I think you are right, the server does start executing the pipeline before receiving the sync message, though there is still something strange going on (but probably on the client side): I have a pipeline of say 500 INSERTs. If I "execute" this pipeline by first sending all the statements and then reading the results, then everything works as expected. This is the call sequence I am talking about: PQsendQueryPrepared() # INSERT #1 PQflush() PQsendQueryPrepared() # INSERT #2 PQflush() ... PQsendQueryPrepared() # INSERT #500 PQpipelineSync() PQflush() PQconsumeInput() PQgetResult() # INSERT #1 PQgetResult() # NULL PQgetResult() # INSERT #2 PQgetResult() # NULL ... PQgetResult() # INSERT #500 PQgetResult() # NULL PQgetResult() # PGRES_PIPELINE_SYNC If, however, I execute it by checking for results before sending the next INSERT, I get the following call sequence: PQsendQueryPrepared() # INSERT #1 PQflush() PQsendQueryPrepared() # INSERT #2 PQflush() ... PQsendQueryPrepared() # INSERT #~400 PQflush() PQconsumeInput() # At this point select() indicates we can read. PQgetResult() # NULL (???) PQgetResult() # INSERT #1 PQgetResult() # NULL PQgetResult() # INSERT #2 PQgetResult() # NULL ... What's strange here is that the first PQgetResult() call (marked with ???) returns NULL instead of result for INSERT #1 as in the first call sequence. Interestingly, if I skip it, the rest seems to progress as expected. Any idea what might be going on here? My hunch is that there is an issue with libpq's state machine. In particular, in the second case, PQgetResult() is called before the sync message is sent. Did you have a chance to test such a scenario (i.e., a large pipeline where the first result is processed before the PQpipelineSync() call)? Of course, this could very well be a bug on my side or me misunderstanding something.