|
15 | 15 | from . import DEFAULT_PORT |
16 | 16 | from .conn import Connection, Connections |
17 | 17 | from .conn import choose_connection, wait_for_connection |
18 | | -from .exc import NoConnectionError |
| 18 | +from .exc import NoConnectionError, WebSocketNotOpen |
19 | 19 | from .proc import Processes, Process, Result |
20 | 20 | from procstar import proto |
| 21 | +from procstar.lib.py import retry_exception |
21 | 22 | from procstar.lib.time import now |
22 | 23 |
|
23 | 24 | FROM_ENV = object() |
@@ -199,7 +200,11 @@ async def _serve_connection(self, access_token, reconnect_timeout, ws): |
199 | 200 | # Add or re-add the connection. |
200 | 201 | try: |
201 | 202 | conn = self.connections._add( |
202 | | - register_msg.conn, register_msg.proc, register_msg.shutdown_state, time, ws |
| 203 | + register_msg.conn, |
| 204 | + register_msg.proc, |
| 205 | + register_msg.shutdown_state, |
| 206 | + time, |
| 207 | + ws, |
203 | 208 | ) |
204 | 209 | conn.info.stats.num_received += 1 # the Register message |
205 | 210 | except RuntimeError as exc: |
@@ -311,13 +316,21 @@ async def request_start( |
311 | 316 | except AttributeError: |
312 | 317 | pass |
313 | 318 |
|
314 | | - conn = await choose_connection( |
315 | | - self.connections, |
316 | | - group_id, |
317 | | - timeout=conn_timeout, |
318 | | - ) |
319 | | - |
320 | | - await conn.send(proto.ProcStartRequest(specs={proc_id: spec})) |
| 319 | + async def choose_and_start(): |
| 320 | + conn = await choose_connection( |
| 321 | + self.connections, |
| 322 | + group_id, |
| 323 | + timeout=conn_timeout, |
| 324 | + ) |
| 325 | + # raises a WebSocketNotOpen if the conn was closed after choosing the |
| 326 | + # connection |
| 327 | + await conn.send(proto.ProcStartRequest(specs={proc_id: spec})) |
| 328 | + return conn |
| 329 | + |
| 330 | + # We try twice sequentially to avoid an unfortunately common case where an agent |
| 331 | + # disconnects directly after the connection is chosen. This is prone to |
| 332 | + # happening after the event loop is unintentionally blocked for period of time. |
| 333 | + conn = await retry_exception(choose_and_start, (WebSocketNotOpen,), retries=1, interval=0) |
321 | 334 | return self.processes.create(conn, proc_id) |
322 | 335 |
|
323 | 336 | async def start(self, *args, **kw_args) -> (Process, Result): |
|
0 commit comments