Skip to content

Commit

Permalink
Proof of concept inplementation of get_run via api.
Browse files Browse the repository at this point in the history
/api/serialize_run hosted by the main instance can be invoked
by a secondary instance to receive a copy of the run from the
cache.

In this way there is never an issue that the run might not
be up to date.

Sadly I don't know how feasible this. A typical serialized run
is 7k and that is probably too much.

Note: currently /api/serialize_run is completely open. It should
be restricted to be only accessible from localhost.
  • Loading branch information
vdbergh committed May 8, 2024
1 parent 7209a4c commit fa16af5
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 3 deletions.
1 change: 1 addition & 0 deletions server/fishtest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def group_finder(username, request):
config.add_route("api_get_elo", "/api/get_elo/{id}")
config.add_route("api_actions", "/api/actions")
config.add_route("api_calc_elo", "/api/calc_elo")
config.add_route("api_serialize_run", "/api/serialize_run/{id}")

config.scan()
return config.make_wsgi_app()
26 changes: 24 additions & 2 deletions server/fishtest/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from fishtest.schemas import api_access_schema, api_schema, gzip_data
from fishtest.stats.stat_util import SPRT_elo, get_elo
from fishtest.util import worker_name
from fishtest.util import serialize, worker_name
from pyramid.httpexceptions import (
HTTPBadRequest,
HTTPFound,
Expand Down Expand Up @@ -90,7 +90,9 @@ def __init__(self, request):

def handle_error(self, error, exception=HTTPBadRequest):
if error != "":
full_url = self.request.route_url(self.request.matched_route.name)
full_url = self.request.route_url(
self.request.matched_route.name, **self.request.matchdict
)
api = urlparse(full_url).path
error = f"{api}: {error}"
print(error, flush=True)
Expand Down Expand Up @@ -290,6 +292,26 @@ def finished_runs(self):
finished[str(run["_id"])] = run
return finished

@view_config(route_name="api_serialize_run")
def serialize_run(self):
self.__t0 = datetime.now(timezone.utc)
try:
run_id = self.request.matchdict["id"]
except Exception as e:
self.handle_error(str(e))
try:
run = self.request.rundb.get_run(run_id)
except Exception as e:
self.handle_error(str(e))
if run is None:
self.handle_error("Run does not exist")
try:
s = serialize(run)
except Exception as e:
self.handle_error(str(e))
result = {"run": s}
return self.add_time(result)

@view_config(route_name="api_actions")
def actions(self):
try:
Expand Down
12 changes: 11 additions & 1 deletion server/fishtest/rundb.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from datetime import datetime, timedelta, timezone

import fishtest.stats.stat_util
import requests
from bson.binary import Binary
from bson.codec_options import CodecOptions
from bson.objectid import ObjectId
Expand All @@ -22,6 +23,7 @@
from fishtest.util import (
GeneratorAsFileReader,
crash_or_time,
deserialize,
estimate_game_duration,
format_bounds,
format_results,
Expand Down Expand Up @@ -381,7 +383,15 @@ def get_run(self, r_id):
}
return run
else:
return self.runs.find_one({"_id": ObjectId(r_id)})
result = requests.get(f"http://localhost/api/serialize_run/{r_id}")
try:
result.raise_for_status()
b = result.json()
run = deserialize(b["run"])
except Exception as e:
print(f"Failed to deserialize {r_id}: {str(e)}", flush=True)
return None
return run

def start_timer(self):
self.timer = threading.Timer(1.0, self.flush_buffers)
Expand Down
17 changes: 17 additions & 0 deletions server/fishtest/util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import base64
import gzip
import hashlib
import math
import re
Expand All @@ -6,6 +8,7 @@
from email.mime.text import MIMEText
from functools import cache

import bson
import fishtest.stats.stat_util
import numpy
import scipy.stats
Expand Down Expand Up @@ -512,3 +515,17 @@ def get_hash(s):
if h:
return int(h.group(1))
return 0


def serialize(run):
b = bson.encode(run)
g = gzip.compress(b)
b64 = base64.b64encode(g).decode()
return b64


def deserialize(run_b64):
g = base64.b64decode(run_b64)
b = gzip.decompress(g)
run = bson.decode(b)
return run

0 comments on commit fa16af5

Please sign in to comment.