Problem Statement
During the extension driver resilience audit, three edge-case failure modes were identified in JsonRpcStdioClient and PluginRpcBridge:
- Unbounded
_stdin.flush(): In JsonRpcStdioClient.sendRequest, await _stdin.flush() has no timeout. If a child process deadlocks or stops draining stdin, when the OS pipe buffer (~64KB) fills up, _stdin.flush() hangs indefinitely without triggering request timeout.
- Watchdog Deadlock Delay: When
SandboxWatchdog detects a ping timeout (deadlock), it kills the handle, but cancels _exitSub beforehand so PluginRpcBridge does not receive the exit event to abort in-flight requests immediately with PluginDeadlockException.
- Short Request Timeout vs Manifest: Extensions like
clickhouse-query-ext specify analytical timeouts (e.g. timeout_seconds: 600), but PluginRpcBridge hardcodes requestTimeout = const Duration(seconds: 30).
- Handshake Timeout Cushion: A strict 3s handshake timeout can cause false positives during cold start or bwrap setup on slow systems; also does not include stderr lines when timing out.
Proposed Solution
- Wrap
_stdin.flush() with a bounded timeout (Duration(seconds: 3)) in JsonRpcStdioClient.
- In
PluginRpcBridge.onStopped, if reason is deadlock, immediately call _failPending(PluginDeadlockException(...)) and dispose.
- Configure
requestTimeout in PluginRpcBridge based on manifest.sandbox?.permissions?.resources?.timeoutSeconds when available.
- Increase default handshake timeout to 6 seconds and capture last stderr lines on handshake timeout.
Problem Statement
During the extension driver resilience audit, three edge-case failure modes were identified in
JsonRpcStdioClientandPluginRpcBridge:_stdin.flush(): InJsonRpcStdioClient.sendRequest,await _stdin.flush()has no timeout. If a child process deadlocks or stops draining stdin, when the OS pipe buffer (~64KB) fills up,_stdin.flush()hangs indefinitely without triggering request timeout.SandboxWatchdogdetects a ping timeout (deadlock), it kills the handle, but cancels_exitSubbeforehand soPluginRpcBridgedoes not receive the exit event to abort in-flight requests immediately withPluginDeadlockException.clickhouse-query-extspecify analytical timeouts (e.g.timeout_seconds: 600), butPluginRpcBridgehardcodesrequestTimeout = const Duration(seconds: 30).Proposed Solution
_stdin.flush()with a bounded timeout (Duration(seconds: 3)) inJsonRpcStdioClient.PluginRpcBridge.onStopped, if reason isdeadlock, immediately call_failPending(PluginDeadlockException(...))and dispose.requestTimeoutinPluginRpcBridgebased onmanifest.sandbox?.permissions?.resources?.timeoutSecondswhen available.