"""Spooler module loaded directly by uWSGI spooler process.""" import importlib import json from time import time import traceback import sentry_sdk import uwsgi # noqa CACHE_NAME = "uwsgi_cache" MAX_RETRIES = 3 def spooler_log(*args): """Spooler log.""" print("[spooler]", *args) def spooler(args: dict): """Execute jobs queued with the uWSGI spooler.""" TASK_ID = args.get(b"task_id") # Abort this task if it doesn't have an ID if not TASK_ID: spooler_log("Aborting task as it did not have an ID.") return uwsgi.SPOOL_OK try: # Execute the task start = float(args[b"start"].decode("utf-8")) module = importlib.import_module(args[b"module"].decode("utf-8")) cls = getattr(module, args[b"class"].decode("utf-8")) func = getattr(cls, args[b"func"].decode("utf-8")) args = json.loads(args[b"args"].decode("utf-8")) func(*args) spooler_log(f"Task({TASK_ID}) completed in {round(time() - start, 2)}secs.") uwsgi.cache_del(TASK_ID, CACHE_NAME) return uwsgi.SPOOL_OK except Exception as e: spooler_log(f"Error occurred in task({TASK_ID}):", e) # Fetch retries remaining from the cache - if we're out of retries, abort if not uwsgi.cache_exists(TASK_ID, CACHE_NAME): uwsgi.cache_set( TASK_ID, MAX_RETRIES.to_bytes(1, byteorder="big"), 0, CACHE_NAME, ) cached_data = uwsgi.cache_get(TASK_ID, CACHE_NAME) retries = int.from_bytes(cached_data, byteorder="big") - 1 if cached_data else 0 if retries <= 0: spooler_log("Aborting as there are no retries remaining.") uwsgi.cache_del(TASK_ID, CACHE_NAME) return uwsgi.SPOOL_OK spooler_log(f"Retries remaining: {retries}.") traceback.print_exc() sentry_sdk.capture_exception(e) uwsgi.cache_update( TASK_ID, retries.to_bytes(1, byteorder="big"), 0, CACHE_NAME, ) return uwsgi.SPOOL_RETRY uwsgi.spooler = spooler