Fix crash-loop: CronTrigger resolves its own timezone independent of the scheduler's
All checks were successful
Deploy / deploy (push) Successful in 22s
All checks were successful
Deploy / deploy (push) Successful in 22s
The America/Phoenix TZ fix earlier only passed timezone= to BackgroundScheduler's constructor. That wasn't enough: APScheduler's CronTrigger independently calls tzlocal's get_localzone() in its own __init__ when not given an explicit timezone, regardless of what the scheduler itself is configured with. This never surfaced in testing because schedules.json was always empty — the rebuild_jobs() loop that constructs CronTrigger never executed. It crashed for real once a real schedule existed (confirmed live: the already-configured "Pokemon Singles >10" 4x/day schedule crash-looped the container on every boot). Fixed by passing the same resolved timezone to every CronTrigger call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
This commit is contained in:
parent
3b08af7938
commit
0f336b3d0c
2 changed files with 15 additions and 7 deletions
2
app.py
2
app.py
|
|
@ -8,7 +8,7 @@ import automation
|
||||||
import scheduler
|
import scheduler
|
||||||
import storage
|
import storage
|
||||||
|
|
||||||
APP_VERSION = "0.4.0"
|
APP_VERSION = "0.4.1"
|
||||||
APP_VERSION_DATE = "2026-09-01"
|
APP_VERSION_DATE = "2026-09-01"
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
|
||||||
20
scheduler.py
20
scheduler.py
|
|
@ -20,12 +20,19 @@ import storage
|
||||||
logger = logging.getLogger("massprice.scheduler")
|
logger = logging.getLogger("massprice.scheduler")
|
||||||
|
|
||||||
_work_queue: "queue.Queue" = queue.Queue()
|
_work_queue: "queue.Queue" = queue.Queue()
|
||||||
# Timezone must be set at construction time, not via a later .configure()
|
|
||||||
# call — APScheduler resolves a timezone immediately in __init__ via
|
# Resolved once and reused everywhere a timezone is needed. Must be
|
||||||
# tzlocal's system lookup, which fails inside this container image for
|
# passed explicitly at construction time, never left to be inferred —
|
||||||
# an arbitrary $TZ (no zoneinfo/tzdata wiring), so the explicit TZ env
|
# both BackgroundScheduler AND CronTrigger independently call tzlocal's
|
||||||
# var has to win before that lookup ever runs.
|
# get_localzone() in their own __init__ if not given one, which fails
|
||||||
_scheduler = BackgroundScheduler(timezone=pytz.timezone(os.environ.get("TZ", "UTC")))
|
# inside this container image for an arbitrary $TZ (no zoneinfo/tzdata
|
||||||
|
# wiring). Passing it to the scheduler alone is NOT enough — a
|
||||||
|
# CronTrigger built without its own explicit timezone still resolves
|
||||||
|
# get_localzone() itself, regardless of what the scheduler is configured
|
||||||
|
# with, and crashes the same way. Every CronTrigger(...) below must pass
|
||||||
|
# timezone=_TZ explicitly.
|
||||||
|
_TZ = pytz.timezone(os.environ.get("TZ", "UTC"))
|
||||||
|
_scheduler = BackgroundScheduler(timezone=_TZ)
|
||||||
_started = False
|
_started = False
|
||||||
|
|
||||||
VALID_DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
|
VALID_DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
|
||||||
|
|
@ -69,6 +76,7 @@ def rebuild_jobs():
|
||||||
day_of_week=",".join(days),
|
day_of_week=",".join(days),
|
||||||
hour=int(hour),
|
hour=int(hour),
|
||||||
minute=int(minute),
|
minute=int(minute),
|
||||||
|
timezone=_TZ,
|
||||||
)
|
)
|
||||||
_scheduler.add_job(
|
_scheduler.add_job(
|
||||||
enqueue_run,
|
enqueue_run,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue