Problem
The speech-to-text (ASR) microservice in apps/ml/routers/asr.py handles real-time audio streams via WebSockets. To transcode arbitrary browser audio formats, it spawns an ffmpeg subprocess using subprocess.Popen.
When the WebSocket disconnects (or finishes), session.close() is called, which invokes StreamingAudioDecoder.close(). This method calls _wait_for_process_exit() (line 548).
_wait_for_process_exit contains multiple synchronous, blocking calls to self._process.wait(timeout=0.2). Because FastAPI/Uvicorn runs on a single event loop thread, executing blocking code like subprocess.wait() halts the entire event loop. If a client disconnects abruptly, the event loop freezes for up to 0.6 seconds. This prevents the server from handling any other requests, creating a critical performance bottleneck and potential Denial of Service (DoS) vulnerability.
Proposed Changes
- Modify
apps/ml/routers/asr.py:
- Avoid synchronous blocking waits on the event loop thread.
- Use FastAPI/anyio's thread offloader to run the close/wait process in a worker thread:
from anyio.to_thread import run_sync
await run_sync(self.decoder.close)
- Alternatively, use Python's
asyncio.create_subprocess_exec to manage the subprocess asynchronously and await process.wait() natively.
Acceptance Criteria
- WebSocket disconnections do not block the FastAPI event loop.
- The
StreamingAudioDecoder process terminates cleanly without leaving zombie processes.
- High-concurrency tests show that multiple simultaneous WebSocket connections/disconnections do not degrade HTTP route response times.
⚠️ Contributor Instructions (Please Read Carefully)
- Do NOT spam "/assign" or "Please assign me".
- To claim this task, you MUST reply with a brief proposed implementation plan/approach. What files will you touch? How will you solve it?
- Once your approach is reviewed and approved by a maintainer, you will be officially assigned.
- Any PR opened without prior assignment and approach approval will be closed.
Problem
The speech-to-text (ASR) microservice in
apps/ml/routers/asr.pyhandles real-time audio streams via WebSockets. To transcode arbitrary browser audio formats, it spawns anffmpegsubprocess usingsubprocess.Popen.When the WebSocket disconnects (or finishes),
session.close()is called, which invokesStreamingAudioDecoder.close(). This method calls_wait_for_process_exit()(line 548)._wait_for_process_exitcontains multiple synchronous, blocking calls toself._process.wait(timeout=0.2). Because FastAPI/Uvicorn runs on a single event loop thread, executing blocking code likesubprocess.wait()halts the entire event loop. If a client disconnects abruptly, the event loop freezes for up to 0.6 seconds. This prevents the server from handling any other requests, creating a critical performance bottleneck and potential Denial of Service (DoS) vulnerability.Proposed Changes
apps/ml/routers/asr.py:asyncio.create_subprocess_execto manage the subprocess asynchronously andawait process.wait()natively.Acceptance Criteria
StreamingAudioDecoderprocess terminates cleanly without leaving zombie processes.