Async SSE client reuses httpx.AsyncClient across event loops -> "Event loop is closed"
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
Línea de trabajo
Empieza en src/conductor/client/orkes/orkes_agent_client.py, en OrkesAgentClient._get_sse_async_client(), y ejecuta después la reproducción two-asyncio.run del issue contra el servidor local de keep-alive. El trabajo está terminado cuando el cliente asíncrono no se reutiliza entre bucles de eventos y ambas llamadas devuelven 200 sin RuntimeError: Event loop is closed.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Re-scoped from conductor-oss/conductor#1563 (originally agentspan-ai/agentspan#318). That one was filed against the server repo by the Agentspan migration, but the code actually lives here in the Python SDK.
OrkesAgentClient._get_sse_async_client() (src/conductor/client/orkes/orkes_agent_client.py:274) caches one httpx.AsyncClient and only recreates it when is_closed is true. The catch: a client whose connection pool is bound to a closed event loop is not is_closed -- so it gets reused. Drive the async streaming API through a fresh asyncio.run(...) twice in one process (each asyncio.run spins up and tears down its own loop) and the second call dies with RuntimeError: Event loop is closed. The client is only reset in shutdown_async(), never per-stream.
Good news up front: the original repro (sync start() called twice) no longer reproduces -- the sync SSE path was rewritten on the synchronous requests library, so it never touches an async client. This is confined to the async API (stream_async() / _get_sse_async_client()).
Repro that drives the real getter (a tiny local keep-alive server is all it needs so a connection actually gets pooled and bound to loop A):
import asyncio, threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from conductor.client.configuration.configuration import Configuration
from conductor.client.orkes.orkes_agent_client import OrkesAgentClient
class H(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1" # keep-alive so httpx pools the connection
def do_GET(self):
b = b"ok"; self.send_response(200)
self.send_header("Content-Length", str(len(b))); self.end_headers(); self.wfile.write(b)
def log_message(self, *a): pass
srv = HTTPServer(("127.0.0.1", 0), H); port = srv.server_address[1]
threading.Thread(target=srv.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{port}/"
client = OrkesAgentClient(Configuration(server_api_url="http://localhost:8080/api"))
async def touch():
c = client._get_sse_async_client()
print("is_closed before use:", c.is_closed)
print("GET ->", (await c.get(url)).status_code)
asyncio.run(touch()) # loop A -- opens + pools a connection bound to loop A
asyncio.run(touch()) # loop B -- reuses the cached client -> boom
Output:
is_closed before use: False
GET -> 200
is_closed before use: False
RuntimeError: Event loop is closed
Fix: in _get_sse_async_client(), also recreate the client when the current running loop differs from the one it was created on (track id(asyncio.get_running_loop()) next to the cached client). Confirmed locally that this makes the repro pass (both calls return 200).
- Lenguaje dominante
- Python
- Estrellas
- 104
- Forks
- 43
- Merge medio
- 1 d 13 h
- PR fusionados (30 d)
- 4
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de conductor-oss/python-sdk
-
Dificultad 1/5 1-3 horas Aptitud para principiantes 90/100
conductor-oss/python-sdk#502 ·
-
bug dependencies
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
conductor-oss/python-sdk#486 ·
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
conductor-oss/python-sdk#483 ·
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
conductor-oss/python-sdk#478 ·
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
conductor-oss/python-sdk#430 ·
Todos los issues de conductor-oss/python-sdk
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100