Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1r94Ni-00DvXV-Lb for pgsql-bugs@arkaria.postgresql.org; Fri, 01 Dec 2023 14:20:39 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1r94Nh-007zHG-95 for pgsql-bugs@arkaria.postgresql.org; Fri, 01 Dec 2023 14:20:37 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1r94Nf-007zH6-PL for pgsql-bugs@lists.postgresql.org; Fri, 01 Dec 2023 14:20:37 +0000 Received: from out5-smtp.messagingengine.com ([66.111.4.29]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1r94Nc-008HZj-M7 for pgsql-bugs@lists.postgresql.org; Fri, 01 Dec 2023 14:20:34 +0000 Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailout.nyi.internal (Postfix) with ESMTP id AB84E5C0223; Fri, 1 Dec 2023 09:20:30 -0500 (EST) Received: from mailfrontend1 ([10.202.2.162]) by compute1.internal (MEProxy); Fri, 01 Dec 2023 09:20:30 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:content-type:date:date:feedback-id:feedback-id :from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1701440430; x= 1701526830; bh=4taGnOC7uz5Y1m/PHirjctPs30DFddG9D9IuPGONTlM=; b=D S0F6Z6VH/atvoWivVDDmzPlYykuDSjrB485yExPp5TgkB8PkuDvaHQBzBy0xRv3y geLbvcWeVuPSY3WJNozZCwd+HZt7dBoK0yJEBfKEbyipprSUeFDEDMAJsiZ7C90B viKg9lxv0+H5CwzGnpAFTF3f7VsRFBtSIjJ5fGGdjbdVRmrC8ts6uEQ+2GDW28yJ cmpt6f/gLGVISWgqs/ixcKPT3WvVHkc81rYyYTo8F0Zi9j38XwzyNKd7lHWRADP0 QigB6f26oJuCK0Ny5q5DyDNdahwuLvjq39UOeE4wIEmmc3N1iGR2oJ0N+XW2YBh4 bmczhsg8eta2uwl0wqR7g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvkedrudeiledgieegucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucenucfjughrpeffhffvvefukfggtggugfgjsehmke erredttdejnecuhfhrohhmpeetlhhvrghrohcujfgvrhhrvghrrgcuoegrlhhvhhgvrhhr vgesrghlvhhhrdhnohdqihhprdhorhhgqeenucggtffrrghtthgvrhhnpeegueehtdeive ffgfefvedufeefieelleekheefueeiieffkedtheeuudeijeetueenucffohhmrghinhep ghhithhhuhgsrdgtohhmpdgvnhhtvghrphhrihhsvggusgdrtghomhenucevlhhushhtvg hrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpegrlhhvhhgvrhhrvgesrghl vhhhrdhnohdqihhprdhorhhg X-ME-Proxy: Feedback-ID: ia2694551:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 1 Dec 2023 09:20:29 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alvh.no-ip.org; s=schmee; t=1701440428; bh=3Wj0la47OgrZM8V7dSXhdRlYtCNf2sc7bGTIfGNcr/k=; h=Date:From:To:Cc:Subject:In-Reply-To:From; b=SBaXESO6lxIG9wGgN47ccC8W8/LlgpR+lIABh7dgguN3Gnpid/JMVhh4b/fID0dCg yQH408SmMUj0LkysQm9nGmgchKeVdME7KRoIvjaiMnzPAxqWCHsgA5P0Haeevx7qRp WHybjR4hF8CIviyvk+pavvz1pdkBexbbqelId3eWscX0HB1wvV1a1pKMN0dTZoWNKB jebcHBiJLisa0EmXDENe4dvR/ICzz0BgmUoz86Uk3lZfdSHlbI8rlSFAXcnwSG9toP 980wL3qWesGplvTZtuS8XgMwIUUsuRzjNOlK8hpmAaj1DQl+gk2LxqYiVdKnlqnbvl DAZZMi29CuMAQ== Received: by perhan.alvh.no-ip.org (Postfix, from userid 1000) id 128AC383; Fri, 1 Dec 2023 15:20:28 +0100 (CET) Date: Fri, 1 Dec 2023 15:20:28 +0100 From: Alvaro Herrera To: =?utf-8?B?0KLRgNC+0YTQuNC80L7QsiDQmNCy0LDQvQ==?= Cc: "pgsql-bugs@lists.postgresql.org" Subject: Re: BUG #17948: libpq seems to misbehave in a pipelining corner case Message-ID: <202312011420.zmxtoxtvu6ip@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="whoouggmli4wkqib" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <82241691236930@mail.yandex.ru> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --whoouggmli4wkqib Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2023-Aug-05, Трофимов Иван wrote: > We've managed to reproduce client <-> server de-synchronization triggered > by this, which we are seeing in production. > Libpq considers '< 2TDCEZ' a sufficient response to '> BDESS', when > according to specification one more 'Z' is expected. >   > A bit more context and a MRE: > https://github.com/itrofimow/libpq_protocol_desync Does this patch fix the problems? I think the sync loss is because we're consuming elements from the command queue even when we shouldn't, because we don't know for an ERROR whether we should do so or not. What this patch does is a bit of a hack, I think, but it does solve your test case (and it doesn't break anything else in the tree) AFAICS. I may have to play some more with it before gaining confidence, though. Thanks -- Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/ "Hay dos momentos en la vida de un hombre en los que no debería especular: cuando puede permitírselo y cuando no puede" (Mark Twain) --whoouggmli4wkqib Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="libpq.patch" diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 04610ccf5e..f19edaa5fd 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -2112,19 +2112,24 @@ PQgetResult(PGconn *conn) break; case PGASYNC_READY: + res = pqPrepareAsyncResult(conn); /* - * For any query type other than simple query protocol, we advance - * the command queue here. This is because for simple query - * protocol we can get the READY state multiple times before the - * command is actually complete, since the command string can - * contain many queries. In simple query protocol, the queue - * advance is done by fe-protocol3 when it receives ReadyForQuery. + * Tell pqCommandQueueAdvance to do its thing, if not in simple + * query protocol. XXX rewrite + * + * When returning a result that is not an error, for any query type + * other than simple query protocol, we advance the command queue + * here. This is because for simple query protocol we can get the + * READY state multiple times before the command is actually + * complete, since the command string can contain many queries. In + * simple query protocol, the queue advance is done by fe-protocol3 + * when it receives ReadyForQuery. */ if (conn->cmd_queue_head && conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE) - pqCommandQueueAdvance(conn); - res = pqPrepareAsyncResult(conn); + pqCommandQueueAdvance(conn, res->resultStatus == PGRES_FATAL_ERROR); + if (conn->pipelineStatus != PQ_PIPELINE_OFF) { /* @@ -3090,15 +3095,28 @@ PQexitPipelineMode(PGconn *conn) * pqCommandQueueAdvance * Remove one query from the command queue, when we receive * all results from the server that pertain to it. + * + * Note: when the result we're processing is an error, we must only advance + * the queue for message types other than SYNC. This is because we might + * get other errors arbitrarily (say, connection closed), but we must not go + * past the synchronization point until the server agrees that that's where we + * are. */ void -pqCommandQueueAdvance(PGconn *conn) +pqCommandQueueAdvance(PGconn *conn, bool isError) { PGcmdQueueEntry *prevquery; if (conn->cmd_queue_head == NULL) return; + /* + * In case of error, we only allow the queue to advance if the entry + * to be removed is not SYNC. + */ + if (isError && conn->cmd_queue_head->queryclass == PGQUERY_SYNC) + return; + /* delink from queue */ prevquery = conn->cmd_queue_head; conn->cmd_queue_head = conn->cmd_queue_head->next; @@ -3162,7 +3180,8 @@ pqPipelineProcessQueue(PGconn *conn) */ if (conn->cmd_queue_head == NULL) { - conn->asyncStatus = PGASYNC_IDLE; + if (conn->pipelineStatus != PQ_PIPELINE_ABORTED) + conn->asyncStatus = PGASYNC_IDLE; return; } diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 5613c56b14..017999057d 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -246,7 +246,7 @@ pqParseInput3(PGconn *conn) */ if (conn->cmd_queue_head && conn->cmd_queue_head->queryclass == PGQUERY_SIMPLE) - pqCommandQueueAdvance(conn); + pqCommandQueueAdvance(conn, false); conn->asyncStatus = PGASYNC_IDLE; } break; diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 22bc682ffc..8518ac6d76 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -698,7 +698,7 @@ extern void pqSaveMessageField(PGresult *res, char code, extern void pqSaveParameterStatus(PGconn *conn, const char *name, const char *value); extern int pqRowProcessor(PGconn *conn, const char **errmsgp); -extern void pqCommandQueueAdvance(PGconn *conn); +extern void pqCommandQueueAdvance(PGconn *conn, bool isError); extern int PQsendQueryContinue(PGconn *conn, const char *query); /* === in fe-protocol3.c === */ --whoouggmli4wkqib--