Hi,

On 2025-07-16 11:50:46 -0700, Jacob Champion wrote:
> On Wed, Jul 16, 2025 at 11:11 AM Andres Freund <and...@anarazel.de> wrote:
> > If one modifies libpq to use openssl readahead (which does result in 
> > speedups,
> > because otherwise openssl think it's useful to do lots of 5 byte reads from
> > the socket), I see occasional hangs in libpq.
>
> Now that is a very interesting coincidence, because the patch I'm
> working on is about to add an assertion that readahead is turned
> *off*. :D

Heh.


> Based on my understanding of [1], readahead makes this overall problem
> much worse by opportunistically slurping bytes off the wire and doing
> absolutely nothing with them until you call SSL_read() enough times to
> finally get to them.

Right - but it also substantially reduces the number of syscalls :(. I'm not
sure that it's wise to close that door permanently. Clearly it's not something
we need to care about in the back branches.

Without it openssl will do one read for the record length, and another read
for the record contents. I.e. it'll often double the number of syscalls.


Without readahead:

perf stat -e raw_syscalls:sys_enter,cycles,context-switches 
src/bin/pgbench/pgbench -c $c -j$j -M prepared -n -P 1 -f <(echo 'select 1') -h 
localhost -t1000

             5,561      raw_syscalls:sys_enter
        89,125,976      cycles
             1,009      context-switches


With readahead:

             4,556      raw_syscalls:sys_enter
        83,582,571      cycles
             1,007      context-switches



> It would even hide those bytes from the pending
> count:
>
> > Therefore, it is possible for no more bytes to be readable from the
> > underlying BIO (because OpenSSL has already read them) and for
> > SSL_pending() to return 0, even though readable application data bytes
> > are available (because the data is in unprocessed buffered records).

It's rather depressing, like how this this API behaviour came to be?


Luckily there seems to be SSL_has_pending():

> SSL_has_pending() returns 1 if s has buffered data (whether processed or
> unprocessed) and 0 otherwise

And it seems old enough:

> The SSL_has_pending() function was added in OpenSSL 1.1.0.


> > It does seem ... weird ... that pqGetCopyData3() just processes whatever is
> > already in the libpq buffer and then waits for the socket to become 
> > readable,
> > before ever calling pqReadData(). What guarantees, even without the change
> > above, that a) there's no pending data inside openssl b) that we're not
> > waiting for a write?
>
> (a) is the bug discussed here, AFAICT?

I'm not entirely sure, tbh.


> > If I make pqGetCopyData3() call pqReadData() before the pqWait() and 
> > continue
> > if it returns 1, the hang vanishes.
>
> Sure, but that's cheating. I think I'd expect that adding extra
> "unnecessary" reads would help cover up the problem that readahead has
> caused.

To me the pattern in our code seems bogus, even without readahead, no?

getCopyDataMessage() can return 0 because our internal buffer is empty,
without ever reading from the socket or openssl. But if the SSL record
contained two messages (or parts of two messages), all the required data may
be in openssl. In which case the pqWait() that pqGetCopyData3() does will wait
forever.  That's a problem unrelated to readahead, no?

I.e. the extra read actually ensures that we're not doing a pqWait() without
knowing whether we need one.

Greetings,

Andres Freund


Reply via email to