Build and Push Docker Container / build-and-push (push) Successful in 2m18s
- Register Zenith 1.1 with balanced duel verdicts and forced-outcome overrides. - Prioritize permanent-trail space in Constrictor and health-aware hazard routes. - Deduct executor queue delays and return fallback directions on move timeouts. - Correlate response headers, replay timings, and sampled structured trace logs. - Move shared metrics publication off the response path without losing local counts. - Allow configurable tournament opponents and add a psql helper recipe. - Preserve empty latency values through compact replay encoding and migration. - Update Arena game links and use default assets for missing customizations. - Cover strategy, HTTP lifecycle, tracing, replay codecs, and dashboard behavior. - Validate with 260 passing Python tests, 4 skips, and 2 passing JavaScript tests.
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Small request-local timings; no board payloads or external tracing dependency."""
|
|
from contextlib import contextmanager
|
|
import json
|
|
import os
|
|
from threading import Lock
|
|
from time import perf_counter
|
|
from uuid import uuid4
|
|
|
|
class MoveTrace:
|
|
def __init__(self, game_id, turn):
|
|
self.id = uuid4().hex
|
|
self.game_id = game_id
|
|
self.turn = turn
|
|
self.started = perf_counter()
|
|
self._lock = Lock()
|
|
self._fields = {}
|
|
|
|
def set(self, **fields):
|
|
with self._lock:
|
|
self._fields.update(fields)
|
|
|
|
@contextmanager
|
|
def stage(self, name):
|
|
started = perf_counter()
|
|
try:
|
|
yield
|
|
finally:
|
|
self.set(**{name + '_ms': round((perf_counter() - started) * 1000, 3)})
|
|
|
|
def snapshot(self):
|
|
with self._lock:
|
|
return {'trace_id': self.id, **self._fields}
|
|
|
|
def log(self, logger):
|
|
fields = self.snapshot()
|
|
# Every timeout/slow request, plus deterministic sampling of normal moves.
|
|
try:
|
|
sample = max(0, int(os.getenv('MOVE_TRACE_SAMPLE_EVERY', '100')))
|
|
slow_ms = max(0, float(os.getenv('MOVE_TRACE_SLOW_MS', '300')))
|
|
except ValueError:
|
|
sample, slow_ms = 100, 300
|
|
slow_stage = any(value >= slow_ms for key, value in fields.items()
|
|
if key.endswith('_ms') and isinstance(value, (int, float)))
|
|
if not (fields.get('timed_out') or fields.get('metrics_publish_failed') or slow_stage
|
|
or (sample and int(self.id[:8], 16) % sample == 0)):
|
|
return
|
|
logger.info('move_trace %s', json.dumps({
|
|
'game_id': self.game_id, 'turn': self.turn, **fields,
|
|
}, separators=(',', ':')))
|