Upload 12 files
Browse files
src/agentcore/orchestrator.py
CHANGED
|
@@ -184,6 +184,22 @@ def analytics_log_status() -> dict:
|
|
| 184 |
}
|
| 185 |
|
| 186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
async def _send_log_payload(worker_url: str, token: str, payload: dict):
|
| 188 |
"""POST one analytics payload.
|
| 189 |
|
|
@@ -208,6 +224,34 @@ async def _send_log_payload(worker_url: str, token: str, payload: dict):
|
|
| 208 |
return resp.status_code, report
|
| 209 |
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
async def _flush_pending(worker_url: str, token: str, max_items: int = 5):
|
| 212 |
"""Re-attempt parked payloads oldest-first; stop at the first failure so a
|
| 213 |
still-blocked Worker is not hammered."""
|
|
@@ -272,7 +316,7 @@ def _log_agent_query(question: str, response: AgentResponse, start_time: float)
|
|
| 272 |
except Exception:
|
| 273 |
pass
|
| 274 |
try:
|
| 275 |
-
status, report = await
|
| 276 |
except Exception as _exc:
|
| 277 |
_PENDING_LOGS.append(_payload)
|
| 278 |
_record_attempt("unreachable", f"{type(_exc).__name__}: {_exc}")
|
|
@@ -320,6 +364,8 @@ def _log_agent_query(question: str, response: AgentResponse, start_time: float)
|
|
| 320 |
|
| 321 |
if not _spawn_background(_post_to_worker()):
|
| 322 |
logger.warning("D1 logging skipped: no running event loop")
|
|
|
|
|
|
|
| 323 |
except Exception:
|
| 324 |
pass
|
| 325 |
|
|
|
|
| 184 |
}
|
| 185 |
|
| 186 |
|
| 187 |
+
async def _send_with_retry(worker_url: str, token: str, payload: dict,
|
| 188 |
+
attempts: int = 2, backoff: float = 2.0):
|
| 189 |
+
"""_send_log_payload with one retry after a short backoff. Connectivity to
|
| 190 |
+
workers.dev from this host drops for short windows; a single immediate
|
| 191 |
+
retry recovers a meaningful fraction of those without involving the buffer."""
|
| 192 |
+
last_exc = None
|
| 193 |
+
for i in range(attempts):
|
| 194 |
+
try:
|
| 195 |
+
return await _send_log_payload(worker_url, token, payload)
|
| 196 |
+
except Exception as exc:
|
| 197 |
+
last_exc = exc
|
| 198 |
+
if i + 1 < attempts:
|
| 199 |
+
await asyncio.sleep(backoff)
|
| 200 |
+
raise last_exc
|
| 201 |
+
|
| 202 |
+
|
| 203 |
async def _send_log_payload(worker_url: str, token: str, payload: dict):
|
| 204 |
"""POST one analytics payload.
|
| 205 |
|
|
|
|
| 224 |
return resp.status_code, report
|
| 225 |
|
| 226 |
|
| 227 |
+
_FLUSHER_STARTED = {"on": False}
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def _ensure_periodic_flusher(worker_url: str, token: str) -> None:
|
| 231 |
+
"""Start (once) a background loop that retries buffered rows every 90s, so
|
| 232 |
+
parked writes land as soon as connectivity returns instead of waiting for
|
| 233 |
+
the next user query. The task holds a strong reference via _spawn_background.
|
| 234 |
+
Lost on container restart - like the buffer itself - which the keep-alive
|
| 235 |
+
pings are there to make rare."""
|
| 236 |
+
if _FLUSHER_STARTED["on"]:
|
| 237 |
+
return
|
| 238 |
+
|
| 239 |
+
async def _loop():
|
| 240 |
+
while True:
|
| 241 |
+
await asyncio.sleep(90)
|
| 242 |
+
if _PENDING_LOGS:
|
| 243 |
+
try:
|
| 244 |
+
n = await _flush_pending(worker_url, token, max_items=10)
|
| 245 |
+
if n:
|
| 246 |
+
_record_attempt("flushed", f"{n} buffered row(s) delivered")
|
| 247 |
+
except Exception:
|
| 248 |
+
pass
|
| 249 |
+
|
| 250 |
+
if _spawn_background(_loop()):
|
| 251 |
+
_FLUSHER_STARTED["on"] = True
|
| 252 |
+
logger.info("Analytics flusher started (90s interval)")
|
| 253 |
+
|
| 254 |
+
|
| 255 |
async def _flush_pending(worker_url: str, token: str, max_items: int = 5):
|
| 256 |
"""Re-attempt parked payloads oldest-first; stop at the first failure so a
|
| 257 |
still-blocked Worker is not hammered."""
|
|
|
|
| 316 |
except Exception:
|
| 317 |
pass
|
| 318 |
try:
|
| 319 |
+
status, report = await _send_with_retry(worker_url, _token, _payload)
|
| 320 |
except Exception as _exc:
|
| 321 |
_PENDING_LOGS.append(_payload)
|
| 322 |
_record_attempt("unreachable", f"{type(_exc).__name__}: {_exc}")
|
|
|
|
| 364 |
|
| 365 |
if not _spawn_background(_post_to_worker()):
|
| 366 |
logger.warning("D1 logging skipped: no running event loop")
|
| 367 |
+
else:
|
| 368 |
+
_ensure_periodic_flusher(worker_url, _token)
|
| 369 |
except Exception:
|
| 370 |
pass
|
| 371 |
|