WebHost: cleanup generate and hopefully fix SQL concurrency problems

This commit is contained in:
Fabian Dill 2022-07-07 01:38:50 +02:00 committed by Fabian Dill
parent 449bc93307
commit 74b19dc1f5
2 changed files with 21 additions and 21 deletions

View File

@ -154,8 +154,10 @@ def autogen(config: dict):
while 1: while 1:
time.sleep(0.1) time.sleep(0.1)
with db_session: with db_session:
# for update locks the database row(s) during transaction, preventing writes from elsewhere
to_start = select( to_start = select(
generation for generation in Generation if generation.state == STATE_QUEUED) generation for generation in Generation
if generation.state == STATE_QUEUED).for_update()
for generation in to_start: for generation in to_start:
launch_generator(generator_pool, generation) launch_generator(generator_pool, generation)
except AlreadyRunningException: except AlreadyRunningException:

View File

@ -4,7 +4,7 @@ import random
import json import json
import zipfile import zipfile
from collections import Counter from collections import Counter
from typing import Dict, Optional as TypeOptional from typing import Dict, Optional, Any
from Utils import __version__ from Utils import __version__
from flask import request, flash, redirect, url_for, session, render_template from flask import request, flash, redirect, url_for, session, render_template
@ -15,7 +15,7 @@ from BaseClasses import seeddigits, get_seed
from Generate import handle_name, PlandoSettings from Generate import handle_name, PlandoSettings
import pickle import pickle
from .models import * from .models import Generation, STATE_ERROR, STATE_QUEUED, commit, db_session, Seed, UUID
from WebHostLib import app from WebHostLib import app
from .check import get_yaml_data, roll_options from .check import get_yaml_data, roll_options
from .upload import upload_zip_to_db from .upload import upload_zip_to_db
@ -30,16 +30,15 @@ def get_meta(options_source: dict) -> dict:
} }
plando_options -= {""} plando_options -= {""}
meta = { server_options = {
"hint_cost": int(options_source.get("hint_cost", 10)), "hint_cost": int(options_source.get("hint_cost", 10)),
"forfeit_mode": options_source.get("forfeit_mode", "goal"), "forfeit_mode": options_source.get("forfeit_mode", "goal"),
"remaining_mode": options_source.get("remaining_mode", "disabled"), "remaining_mode": options_source.get("remaining_mode", "disabled"),
"collect_mode": options_source.get("collect_mode", "disabled"), "collect_mode": options_source.get("collect_mode", "disabled"),
"item_cheat": bool(int(options_source.get("item_cheat", 1))), "item_cheat": bool(int(options_source.get("item_cheat", 1))),
"server_password": options_source.get("server_password", None), "server_password": options_source.get("server_password", None),
"plando_options": list(plando_options)
} }
return meta return {"server_options": server_options, "plando_options": list(plando_options)}
@app.route('/generate', methods=['GET', 'POST']) @app.route('/generate', methods=['GET', 'POST'])
@ -60,13 +59,13 @@ def generate(race=False):
results, gen_options = roll_options(options, meta["plando_options"]) results, gen_options = roll_options(options, meta["plando_options"])
if race: if race:
meta["item_cheat"] = False meta["server_options"]["item_cheat"] = False
meta["remaining_mode"] = "disabled" meta["server_options"]["remaining_mode"] = "disabled"
if any(type(result) == str for result in results.values()): if any(type(result) == str for result in results.values()):
return render_template("checkResult.html", results=results) return render_template("checkResult.html", results=results)
elif len(gen_options) > app.config["MAX_ROLL"]: elif len(gen_options) > app.config["MAX_ROLL"]:
flash(f"Sorry, generating of multiworlds is limited to {app.config['MAX_ROLL']} players for now. " flash(f"Sorry, generating of multiworlds is limited to {app.config['MAX_ROLL']} players. "
f"If you have a larger group, please generate it yourself and upload it.") f"If you have a larger group, please generate it yourself and upload it.")
elif len(gen_options) >= app.config["JOB_THRESHOLD"]: elif len(gen_options) >= app.config["JOB_THRESHOLD"]:
gen = Generation( gen = Generation(
@ -92,23 +91,22 @@ def generate(race=False):
return render_template("generate.html", race=race, version=__version__) return render_template("generate.html", race=race, version=__version__)
def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=None, sid=None): def gen_game(gen_options, meta: Optional[Dict[str, Any]] = None, owner=None, sid=None):
if not meta: if not meta:
meta: Dict[str, object] = {} meta: Dict[str, Any] = {}
meta.setdefault("server_options", {}).setdefault("hint_cost", 10)
race = meta.setdefault("race", False)
meta.setdefault("hint_cost", 10)
race = meta.get("race", False)
del (meta["race"])
plando_options = meta.get("plando", {"bosses", "items", "connections", "texts"})
del (meta["plando_options"])
try: try:
target = tempfile.TemporaryDirectory() target = tempfile.TemporaryDirectory()
playercount = len(gen_options) playercount = len(gen_options)
seed = get_seed() seed = get_seed()
random.seed(seed)
if race: if race:
random.seed() # reset to time-based random source random.seed() # use time-based random source
else:
random.seed(seed)
seedname = "W" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits)) seedname = "W" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))
@ -120,7 +118,8 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
erargs.outputname = seedname erargs.outputname = seedname
erargs.outputpath = target.name erargs.outputpath = target.name
erargs.teams = 1 erargs.teams = 1
erargs.plando_options = PlandoSettings.from_set(plando_options) erargs.plando_options = PlandoSettings.from_set(meta.setdefault("plando_options",
{"bosses", "items", "connections", "texts"}))
name_counter = Counter() name_counter = Counter()
for player, (playerfile, settings) in enumerate(gen_options.items(), 1): for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
@ -136,7 +135,7 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
erargs.name[player] = handle_name(erargs.name[player], player, name_counter) erargs.name[player] = handle_name(erargs.name[player], player, name_counter)
if len(set(erargs.name.values())) != len(erargs.name): if len(set(erargs.name.values())) != len(erargs.name):
raise Exception(f"Names have to be unique. Names: {Counter(erargs.name.values())}") raise Exception(f"Names have to be unique. Names: {Counter(erargs.name.values())}")
ERmain(erargs, seed, baked_server_options=meta) ERmain(erargs, seed, baked_server_options=meta["server_options"])
return upload_to_db(target.name, sid, owner, race) return upload_to_db(target.name, sid, owner, race)
except BaseException as e: except BaseException as e:
@ -148,7 +147,6 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
meta = json.loads(gen.meta) meta = json.loads(gen.meta)
meta["error"] = (e.__class__.__name__ + ": " + str(e)) meta["error"] = (e.__class__.__name__ + ": " + str(e))
gen.meta = json.dumps(meta) gen.meta = json.dumps(meta)
commit() commit()
raise raise