Openembedded Bitbake Development
 help / color / mirror / Atom feed
From: Joshua Watt <jpewhacker@gmail.com>
To: bitbake-devel@lists.openembedded.org
Cc: Joshua Watt <JPEWhacker@gmail.com>
Subject: [bitbake-devel][PATCH v2 06/10] hashserv: server: Add queued streaming API
Date: Thu, 30 Jul 2026 12:30:59 -0600	[thread overview]
Message-ID: <20260730183254.793698-7-JPEWhacker@gmail.com> (raw)
In-Reply-To: <20260730183254.793698-1-JPEWhacker@gmail.com>

Adds an API that allows a stream handler to more precisely control when
a response is sent to the client. The new API does not directly send the
response from the handler to the remote client, but instead the handler
is expected to put the result in a provided queue when the response is
ready. In particular, this allows a stream handler to defer to an
upstream server (utilizing the new client streaming API) in an efficient
way that does not require waiting on a roundtrip with the upstream
server. Instead, several queries to the upstream can be in-flight at
once.

Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
 lib/hashserv/server.py | 96 +++++++++++++++++++++++++++++++++---------
 1 file changed, 75 insertions(+), 21 deletions(-)

diff --git a/lib/hashserv/server.py b/lib/hashserv/server.py
index e7e79196f..0fa81e85d 100644
--- a/lib/hashserv/server.py
+++ b/lib/hashserv/server.py
@@ -229,6 +229,54 @@ def permissions(*permissions, allow_anon=True, allow_self_service=False):
     return wrapper
 
 
+class UpstreamQueue(object):
+    UPSTREAM_NONCE = object()
+
+    def __init__(self, queue, get_local_result, send_upstream, get_upstream_result):
+        self.queue = queue
+        self.pending = []
+        self.cond = asyncio.Condition()
+        self.done = False
+        self.get_local_result = get_local_result
+        self.send_upstream = send_upstream
+        self.get_upstream_result = get_upstream_result
+
+    async def process_results(self):
+        try:
+            while True:
+                async with self.cond:
+                    await self.cond.wait_for(lambda: self.pending or self.done)
+                    if not self.pending:
+                        if self.done:
+                            return
+                        continue
+
+                    value, m = self.pending.pop(0)
+
+                if value is self.UPSTREAM_NONCE:
+                    value = await self.get_upstream_result(m)
+
+                await self.queue.put(value)
+        finally:
+            await self.queue.put(None)
+
+    async def handler(self, m):
+        if m is None:
+            async with self.cond:
+                self.done = True
+                self.cond.notify_all()
+            return
+
+        value = await self.get_local_result(m)
+        if value is None:
+            await self.send_upstream(m)
+            value = self.UPSTREAM_NONCE
+
+        async with self.cond:
+            self.pending.append((value, m))
+            self.cond.notify_all()
+
+
 class ServerClient(bb.asyncrpc.AsyncServerConnection):
     def __init__(self, socket, server):
         super().__init__(socket, "OEHASHEQUIV", server.logger)
@@ -390,35 +438,41 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection):
         validate_unihash(unihash)
         return await self.db.insert_unihash(method, taskhash, unihash)
 
-    async def _stream_handler(self, handler):
+    async def _stream_queue_handler(self, handler, queue):
         await self.socket.send_message("ok")
 
-        while True:
-            upstream = None
+        async def recv():
+            try:
+                while True:
+                    m = await self.socket.recv()
+                    if not m or m == "END":
+                        break
 
-            l = await self.socket.recv()
-            if not l:
-                break
+                    await handler(m)
+            finally:
+                await handler(None)
 
-            try:
-                # This inner loop is very sensitive and must be as fast as
-                # possible (which is why the request sample is handled manually
-                # instead of using 'with', and also why logging statements are
-                # commented out.
-                self.request_sample = self.server.request_stats.start_sample()
-                request_measure = self.request_sample.measure()
-                request_measure.start()
-
-                if l == "END":
+        async def process():
+            while True:
+                m = await queue.get()
+                if m is None:
                     break
 
-                msg = await handler(l)
-                await self.socket.send(msg)
-            finally:
-                request_measure.end()
-                self.request_sample.end()
+                await self.socket.send(m)
 
+        await bb.asyncrpc.TaskGroup.run(recv(), process())
         await self.socket.send("ok")
+
+    async def _stream_handler(self, handler):
+        queue = asyncio.Queue(1000)
+
+        async def h(m):
+            if m is None:
+                await queue.put(None)
+            else:
+                await queue.put(await handler(m))
+
+        await self._stream_queue_handler(h, queue)
         return self.NO_RESPONSE
 
     @permissions(READ_PERM)
-- 
2.54.0



  parent reply	other threads:[~2026-07-30 18:33 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-24 21:25 [bitbake-devel][PATCH 0/6] hashserv: Pipeline Upstream Queries Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 1/6] hashserv: client: Add asynchronous streaming API Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 2/6] hashserv: server: Fix formatting Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 3/6] hashserv: server: Add queued streaming API Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 4/6] hashserv: server: Use streaming and queue API for upstream unihash queries Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 5/6] hashserv: server: Use streaming and queue API for upstream exist queries Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 6/6] hashserv: tests: Add test for upstream pipelining Joshua Watt
2026-07-26 13:50 ` [bitbake-devel][PATCH 0/6] hashserv: Pipeline Upstream Queries Richard Purdie
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 00/10] " Joshua Watt
2026-07-30 18:30   ` [bitbake-devel][PATCH v2 01/10] asyncrpc: Add Task Group Joshua Watt
2026-07-30 18:30   ` [bitbake-devel][PATCH v2 02/10] asyncrpc: serv: Use " Joshua Watt
2026-07-30 18:30   ` [bitbake-devel][PATCH v2 03/10] asyncrpc: serv: Cancel all clients on server stop Joshua Watt
2026-07-30 18:30   ` [bitbake-devel][PATCH v2 04/10] hashserv: tests: Improve test logging Joshua Watt
2026-07-30 18:30   ` [bitbake-devel][PATCH v2 05/10] hashserv: client: Add asynchronous streaming API Joshua Watt
2026-07-30 18:30   ` Joshua Watt [this message]
2026-07-30 18:31   ` [bitbake-devel][PATCH v2 07/10] hashserv: server: Use streaming and queue API for upstream unihash queries Joshua Watt
2026-07-30 18:31   ` [bitbake-devel][PATCH v2 08/10] hashserv: server: Use streaming and queue API for upstream exist queries Joshua Watt
2026-07-30 18:31   ` [bitbake-devel][PATCH v2 09/10] hashserv: tests: Add more upstream tests Joshua Watt
2026-07-30 18:31   ` [bitbake-devel][PATCH v2 10/10] hashserv: tests: Add test for upstream pipelining Joshua Watt
2026-07-31 13:05   ` [PATCH v2 00/10] hashserv: Pipeline Upstream Queries Michal Sieron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260730183254.793698-7-JPEWhacker@gmail.com \
    --to=jpewhacker@gmail.com \
    --cc=bitbake-devel@lists.openembedded.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox