Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions WebHostLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
app.config["SELFHOST"] = True # application process is in charge of running the websites
app.config["GENERATORS"] = 8 # maximum concurrent world gens
app.config["HOSTERS"] = 8 # maximum concurrent room hosters
app.config["ROOM_IDLE_TIMEOUT"] = 2 * 60 * 60 # seconds of idle before a Room spins down
app.config["SELFLAUNCH"] = True # application process is in charge of launching Rooms.
app.config["SELFLAUNCHCERT"] = None # can point to a SSL Certificate to encrypt Room websocket connections
app.config["SELFLAUNCHKEY"] = None # can point to a SSL Certificate Key to encrypt Room websocket connections
Expand Down
1 change: 0 additions & 1 deletion WebHostLib/api/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,5 @@ def supports_apdeltapatch(game: str) -> bool:
"players": get_players(room.seed),
"last_port": room.last_port,
"last_activity": room.last_activity,
"timeout": room.timeout,
"downloads": downloads,
}
1 change: 0 additions & 1 deletion WebHostLib/api/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def get_rooms():
"creation_time": room.creation_time,
"last_activity": room.last_activity,
"last_port": room.last_port,
"timeout": room.timeout,
"tracker": to_url(room.tracker),
})
return jsonify(response)
Expand Down
11 changes: 5 additions & 6 deletions WebHostLib/autolauncher.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,14 @@ def keep_running():
hoster = MultiworldInstance(config, x)
hosters.append(hoster)
hoster.start()

activity_timedelta = timedelta(seconds=config["ROOM_IDLE_TIMEOUT"] + 5)
while not stop_event.wait(0.1):
with db_session:
rooms = select(
room for room in Room if
room.last_activity >= datetime.utcnow() - timedelta(days=3))
room.last_activity >= datetime.utcnow() - activity_timedelta)
for room in rooms:
# we have to filter twice, as the per-room timeout can't currently be PonyORM transpiled.
if room.last_activity >= datetime.utcnow() - timedelta(seconds=room.timeout + 5):
hosters[room.id.int % len(hosters)].start_room(room.id)
hosters[room.id.int % len(hosters)].start_room(room.id)

except AlreadyRunningException:
logging.info("Autohost reports as already running, not starting another.")
Expand Down Expand Up @@ -187,6 +185,7 @@ def __init__(self, config: dict, id: int):
self.cert = config["SELFLAUNCHCERT"]
self.key = config["SELFLAUNCHKEY"]
self.host = config["HOST_ADDRESS"]
self.room_idle_timer = config["ROOM_IDLE_TIMEOUT"]
self.rooms_to_start = multiprocessing.Queue()
self.rooms_shutting_down = multiprocessing.Queue()
self.name = f"MultiHoster{id}"
Expand All @@ -198,7 +197,7 @@ def start(self):
process = multiprocessing.Process(group=None, target=run_server_process,
args=(self.name, self.ponyconfig, get_static_server_data(),
self.cert, self.key, self.host,
self.rooms_to_start, self.rooms_shutting_down),
self.rooms_to_start, self.rooms_shutting_down, self.room_idle_timer),
name=self.name)
process.start()
self.process = process
Expand Down
7 changes: 4 additions & 3 deletions WebHostLib/customserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ def set_up_logging(room_id) -> logging.Logger:

def run_server_process(name: str, ponyconfig: dict, static_server_data: dict,
cert_file: typing.Optional[str], cert_key_file: typing.Optional[str],
host: str, rooms_to_run: multiprocessing.Queue, rooms_shutting_down: multiprocessing.Queue):
host: str, rooms_to_run: multiprocessing.Queue, rooms_shutting_down: multiprocessing.Queue,
room_idle_timeout: int):
from setproctitle import setproctitle

setproctitle(name)
Expand Down Expand Up @@ -316,7 +317,7 @@ async def start_room(room_id):
else:
ctx.logger.exception("Could not determine port. Likely hosting failure.")
with db_session:
ctx.auto_shutdown = Room.get(id=room_id).timeout
ctx.auto_shutdown = room_idle_timeout
if ctx.saving:
setattr(asyncio.current_task(), "save", lambda: ctx._save(True))
assert ctx.shutdown_task is None
Expand Down Expand Up @@ -347,7 +348,7 @@ async def start_room(room_id):
# ensure the Room does not spin up again on its own, minute of safety buffer
room = Room.get(id=room_id)
room.last_activity = datetime.datetime.utcnow() - \
datetime.timedelta(minutes=1, seconds=room.timeout)
datetime.timedelta(minutes=1, seconds=room_idle_timeout)
del room
logging.info(f"Shutting down room {room_id} on {name}.")
finally:
Expand Down
2 changes: 1 addition & 1 deletion WebHostLib/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def host_room(room: UUID):
now = datetime.datetime.utcnow()
# indicate that the page should reload to get the assigned port
should_refresh = ((not room.last_port and now - room.creation_time < datetime.timedelta(seconds=3))
or room.last_activity < now - datetime.timedelta(seconds=room.timeout))
or room.last_activity < now - datetime.timedelta(seconds=app.config["ROOM_IDLE_TIMEOUT"]))

if now - room.last_activity > datetime.timedelta(minutes=1):
# we only set last_activity if needed, otherwise parallel access on /room will cause an internal server error
Expand Down
1 change: 0 additions & 1 deletion WebHostLib/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class Room(db.Entity):
seed = Required('Seed', index=True)
multisave = Optional(buffer, lazy=True)
show_spoiler = Required(int, default=0) # 0 -> never, 1 -> after completion, -> 2 always
timeout = Required(int, default=lambda: 2 * 60 * 60) # seconds since last activity to shutdown
tracker = Optional(UUID, index=True)
# Port special value -1 means the server errored out. Another attempt can be made with a page refresh
last_port = Optional(int, default=lambda: 0)
Expand Down
2 changes: 1 addition & 1 deletion WebHostLib/templates/hostRoom.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
and a <a href="{{ url_for("get_multiworld_sphere_tracker", tracker=room.tracker) }}">Sphere Tracker</a> enabled.
<br />
{% endif %}
The server for this room will be paused after {{ room.timeout//60//60 }} hours of inactivity.
The server for this room will be paused after {{ config["ROOM_IDLE_TIMEOUT"]//60//60 }} hours of inactivity.
Should you wish to continue later,
anyone can simply refresh this page and the server will resume.<br>
{% if room.last_port == -1 %}
Expand Down
24 changes: 3 additions & 21 deletions test/hosting/webhost.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"create_room",
"start_room",
"stop_room",
"set_room_timeout",
"get_multidata_for_room",
"set_multidata_for_room",
"stop_autogen",
Expand Down Expand Up @@ -139,7 +138,6 @@ def stop_room(app_client: "FlaskClient",
from pony.orm import db_session

from WebHostLib.models import Command, Room
from WebHostLib import app

poll_interval = 2

Expand All @@ -152,14 +150,12 @@ def stop_room(app_client: "FlaskClient",
with db_session:
room: Room = Room.get(id=room_uuid)
if simulate_idle:
new_last_activity = datetime.utcnow() - timedelta(seconds=room.timeout + 5)
new_last_activity = datetime.utcnow() - timedelta(seconds=2 * 60 * 60 + 5)
else:
new_last_activity = datetime.utcnow() - timedelta(days=3)
room.last_activity = new_last_activity
address = f"localhost:{room.last_port}" if room.last_port > 0 else None
if address:
original_timeout = room.timeout
room.timeout = 1 # avoid spinning it up again
Command(room=room, commandtext="/exit")

try:
Expand All @@ -186,28 +182,13 @@ def stop_room(app_client: "FlaskClient",
room = Room.get(id=room_uuid)
room.last_port = 0 # easier to detect when the host is up this way
if address:
room.timeout = original_timeout
room.last_activity = new_last_activity
print("timeout restored")


def set_room_timeout(room_id: str, timeout: float) -> None:
from pony.orm import db_session

from WebHostLib.models import Room
from WebHostLib import app

room_uuid = to_python(room_id)
with db_session:
room: Room = Room.get(id=room_uuid)
room.timeout = timeout


def get_multidata_for_room(webhost_client: "FlaskClient", room_id: str) -> bytes:
from pony.orm import db_session

from WebHostLib.models import Room
from WebHostLib import app

room_uuid = to_python(room_id)
with db_session:
Expand All @@ -219,7 +200,6 @@ def set_multidata_for_room(webhost_client: "FlaskClient", room_id: str, data: by
from pony.orm import db_session

from WebHostLib.models import Room
from WebHostLib import app

room_uuid = to_python(room_id)
with db_session:
Expand Down Expand Up @@ -258,9 +238,11 @@ def _stop_webhost_mp(name_filter: str, graceful: bool = True) -> None:
proc.kill()
proc.join()


def stop_autogen(graceful: bool = True) -> None:
# FIXME: this name filter is jank, but there seems to be no way to add a custom prefix for a Pool
_stop_webhost_mp("SpawnPoolWorker-", graceful)


def stop_autohost(graceful: bool = True) -> None:
_stop_webhost_mp("MultiHoster", graceful)
Loading