feat: add live replays and PostgreSQL maintenance tools
Build and Push Docker Container / build-and-push (push) Successful in 7m53s
Build and Push Docker Container / build-and-push (push) Successful in 7m53s
- Stream compact live replay updates across local and clustered dashboards. - Render responsive snake bodies as SVG paths with aligned custom icons. - Add cache-busted assets, replay fallback routes, and live-follow playback. - Support PostgreSQL benchmark sampling and idempotent SQLite migration. - Add dry-run cleanup for old low-quality PostgreSQL replay payloads. - Reward safe perimeter lanes and bump Prism to version 1.5.0. - Add backend, migration, dashboard, and perimeter regression coverage.
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
from statistics import mean, median
|
||||
@@ -13,6 +12,9 @@ from time import perf_counter
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from server.database.benchmark_states import (
|
||||
_build_state, is_postgresql_source, load_postgresql_states,
|
||||
)
|
||||
from server.GameBoard import GameBoard
|
||||
from snakes import SnakeBuilder
|
||||
|
||||
@@ -22,6 +24,9 @@ def percentile(values: list[float], quantile: float) -> float:
|
||||
return ordered[index]
|
||||
|
||||
def load_states(db_path: str, samples: int, stride: int) -> list[tuple[dict, dict]]:
|
||||
if is_postgresql_source(db_path):
|
||||
return load_postgresql_states(db_path, samples, stride)
|
||||
|
||||
connection = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
connection.execute("PRAGMA query_only = ON")
|
||||
max_id = int(connection.execute("SELECT max(id) FROM turns").fetchone()[0] or 0)
|
||||
@@ -55,50 +60,10 @@ def load_states(db_path: str, samples: int, stride: int) -> list[tuple[dict, dic
|
||||
row = connection.execute(query, (next_id,)).fetchone()
|
||||
if row is None:
|
||||
break
|
||||
board = json.loads(row[1])
|
||||
you = json.loads(row[2])
|
||||
if not board.get("snakes"):
|
||||
snakes = []
|
||||
for snake_row in connection.execute(snake_query, (row[9], row[14])):
|
||||
snake_id = snake_row[0]
|
||||
snake_name = snake_row[1] or (row[6] if snake_id == row[5] else snake_id)
|
||||
body = json.loads(snake_row[6])
|
||||
snakes.append({
|
||||
"id": snake_id,
|
||||
"name": snake_name,
|
||||
"health": snake_row[2],
|
||||
"length": snake_row[3],
|
||||
"head": {"x": snake_row[4], "y": snake_row[5]},
|
||||
"body": body,
|
||||
"customizations": json.loads(snake_row[7]),
|
||||
})
|
||||
board = {
|
||||
"width": row[7],
|
||||
"height": row[8],
|
||||
"food": json.loads(row[3]),
|
||||
"hazards": json.loads(row[4]),
|
||||
"snakes": snakes,
|
||||
}
|
||||
if not you:
|
||||
you = next(
|
||||
(snake for snake in board.get("snakes", []) if snake.get("id") == row[5]),
|
||||
{},
|
||||
)
|
||||
if not you or not board.get("snakes"):
|
||||
next_id = int(row[0]) + stride
|
||||
continue
|
||||
metadata = {
|
||||
"game_id": row[9],
|
||||
"source": row[10] or "custom",
|
||||
"map": row[11] or "standard",
|
||||
"ruleset": {
|
||||
"name": row[12] or "standard",
|
||||
"version": row[13] or "v1.0.0",
|
||||
"settings": {},
|
||||
},
|
||||
"turn": int(row[14]),
|
||||
}
|
||||
states.append((board, {"you": you, **metadata}))
|
||||
snake_rows = connection.execute(snake_query, (row[9], row[14])).fetchall()
|
||||
state = _build_state(row, snake_rows)
|
||||
if state is not None:
|
||||
states.append(state)
|
||||
next_id = int(row[0]) + stride
|
||||
connection.close()
|
||||
return states
|
||||
@@ -148,7 +113,10 @@ def benchmark(snake_name: str, states: list[tuple[dict, dict]], repeat: int) ->
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--database", required=True)
|
||||
parser.add_argument(
|
||||
"--database", required=True,
|
||||
help="SQLite path or postgresql:// DSN",
|
||||
)
|
||||
parser.add_argument("--snake", action="append", default=[])
|
||||
parser.add_argument("--samples", type=int, default=100)
|
||||
parser.add_argument("--stride", type=int, default=997)
|
||||
|
||||
Reference in New Issue
Block a user