From d3a1395896f00951e6f2d39ef8112a6400e7e579 Mon Sep 17 00:00:00 2001 From: Deluan Date: Sat, 5 Sep 2026 01:37:39 -0400 Subject: [PATCH] fix(pglite): flush output left after ReadyForQuery before the next request PGlite's interactive_one discards an incoming request when output from the previous frame is still pending ('flush after frame'), which showed up as replies without ReadyForQuery, first after handshakes and then in bursts mid-scan, each one breaking a pooled connection and the scanner transaction on it. After a complete reply the bridge now runs one empty tick and appends anything the backend still had, and logs the message tags of any reply that is still incomplete. --- db/pglite/pglite.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/db/pglite/pglite.go b/db/pglite/pglite.go index bbe7feb46..038e2e5e6 100644 --- a/db/pglite/pglite.go +++ b/db/pglite/pglite.go @@ -472,7 +472,8 @@ func (pg *PGlite) handleConn(conn net.Conn, ioBase string) { } else if packet[0] != 'X' { // Terminate gets no reply by design fixed := ensureReadyForQuery(replies, trapErr) if len(fixed) != len(replies) { - fmt.Fprintf(pg.cfg.Stderr, "# bridge: incomplete reply to %s (trap=%v); synthesized error+ReadyForQuery\n", wireTags(packet), trapErr != nil) + fmt.Fprintf(pg.cfg.Stderr, "# bridge: incomplete reply to %s: got %s (%d bytes, trap=%v); synthesized error+ReadyForQuery\n", + wireTags(packet), wireTags(bytes.Join(replies, nil)), len(bytes.Join(replies, nil)), trapErr != nil) } replies = fixed } @@ -560,7 +561,16 @@ func (pg *PGlite) forwardWire(packet []byte, outFile string) ([][]byte, error) { break } if endsWithReadyForQuery(replies) { - break // a complete response; skip the empty probe tick + // One empty tick: output the backend still holds after ReadyForQuery would otherwise + // make it discard the next request ("flush after frame" in interactive_one.c). + if _, err := pg.fnInteractiveOne.Call(ctx); err == nil { + var extra [][]byte + if pg.collectReply(outFile, &extra) { + fmt.Fprintf(pg.cfg.Stderr, "# bridge: %d bytes after ReadyForQuery (%s)\n", len(bytes.Join(extra, nil)), wireTags(bytes.Join(extra, nil))) + replies = append(replies, extra...) + } + } + break } } return replies, nil