]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
perf: avoid unnecessary recvfrom() in cursor.stream()
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 1 May 2022 22:56:42 +0000 (00:56 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Mon, 2 May 2022 00:53:13 +0000 (02:53 +0200)
Call PQisBusy() before PQconsumeInput() on fetching results. If not
busy, don't call PQconsumeInput() at all but just go to fetching results
and notifications.

This is especially useful in single-row mode because most of the times
the libpq can produce several results after a single network fetch.
Previously we were calling PQconsumeInput() also when results were
already on the client and there was nothing new to read, which forced
the libpq to run a select() to tell apart a lack of data from an EOF,
see `the grumble`_, and caused the overhead reported in #286.

Close #286.

.. _the grumble: https://github.com/postgres/postgres/blob/ed57cac84d1c5642737dab1e4c4b8cb4f0c4305f/src/interfaces/libpq/fe-misc.c#L681

docs/news.rst
psycopg/psycopg/generators.py
psycopg_c/psycopg_c/_psycopg/generators.pyx

index 3a3d0b897001ad4cec8b2f6190e0092efdfd2d96..24c9cb1ae6159f10eb8b59dd20b772575b3d1917 100644 (file)
@@ -25,6 +25,12 @@ Psycopg 3.1 (unreleased)
 - Drop support for Python 3.6.
 
 
+Psycopg 3.0.13 (unreleased)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Fix `Cursor.stream()` slowness (:ticket:`#286`).
+
+
 Current release
 ---------------
 
index 4dd2d72eceecda01bb4ad91335086ba925677d32..86b5a0bba4225fcf4dd7477f2bd39a569b4f8053 100644 (file)
@@ -143,14 +143,16 @@ def fetch(pgconn: PGconn) -> PQGen[Optional[PGresult]]:
 
     Return a result from the database (whether success or error).
     """
-    while 1:
-        pgconn.consume_input()
-        if not pgconn.is_busy():
-            break
+    if pgconn.is_busy():
         yield Wait.R
+        while True:
+            pgconn.consume_input()
+            if not pgconn.is_busy():
+                break
+            yield Wait.R
 
     # Consume notifies
-    while 1:
+    while True:
         n = pgconn.notifies()
         if not n:
             break
index 473e0ea8a7411788b87d1d8ef8ed4dbd47bd012c..4afb53508be69554b5c35a0e810b95bf7b3abbd3 100644 (file)
@@ -159,18 +159,20 @@ def fetch(pq.PGconn pgconn) -> PQGen[Optional[PGresult]]:
     cdef object notify_handler = pgconn.notify_handler
     cdef libpq.PGresult *pgres
 
-    while 1:
-        with nogil:
-            cires = libpq.PQconsumeInput(pgconn_ptr)
-            if cires == 1:
-                ibres = libpq.PQisBusy(pgconn_ptr)
-
-        if 1 != cires:
-            raise e.OperationalError(
-                f"consuming input failed: {error_message(pgconn)}")
-        if not ibres:
-            break
+    if libpq.PQisBusy(pgconn_ptr):
         yield WAIT_R
+        while 1:
+            with nogil:
+                cires = libpq.PQconsumeInput(pgconn_ptr)
+                if cires == 1:
+                    ibres = libpq.PQisBusy(pgconn_ptr)
+
+            if 1 != cires:
+                raise e.OperationalError(
+                    f"consuming input failed: {error_message(pgconn)}")
+            if not ibres:
+                break
+            yield WAIT_R
 
     # Consume notifies
     if notify_handler is not None: