This commit is contained in:
喆OvO
2026-08-12 09:50:37 +08:00
parent cc43873ad2
commit 15b54f204e
10 changed files with 360 additions and 41 deletions
+36 -16
View File
@@ -26,11 +26,16 @@ _TLS = threading.local()
def set_thread_user(user_id):
"""Set the current user for this thread (scheduler per-user iteration)."""
_TLS.user_id = user_id
try:
del _TLS.tz
except AttributeError:
pass
def clear_thread_user():
try:
del _TLS.user_id
del _TLS.tz
except AttributeError:
pass
@@ -78,30 +83,45 @@ def _load_tz() -> "pytz.BaseTzInfo":
def get_tz() -> "pytz.BaseTzInfo":
"""Return the configured timezone object. Cached per user in app config."""
"""Return the configured timezone object.
Cached per-request (Flask ``g``) and per-thread (scheduler TLS), NOT per
process: a timezone change made on one Gunicorn worker must be visible to
the next request on ANY worker immediately (a process-wide cache would
serve stale timezones to other workers).
"""
try:
from flask import current_app
uid = _ctx_user_id()
if hasattr(current_app, "config") and "_timezone_cache" in current_app.config:
cache = current_app.config["_timezone_cache"]
if uid in cache:
return cache[uid]
tz = _load_tz()
if hasattr(current_app, "config"):
cache = current_app.config.setdefault("_timezone_cache", {})
cache[uid] = tz
return tz
from flask import has_request_context, g
if has_request_context():
cached = getattr(g, "_tz_cache", None)
if cached is not None:
return cached
tz = _load_tz()
g._tz_cache = tz
return tz
except Exception:
return pytz.timezone(_DEFAULT_TZ_STR)
pass
# No request context (scheduler threads): cache per thread-user so each
# user's jobs use their own timezone without a DB hit per call.
tz = getattr(_TLS, "tz", None)
if tz is not None:
return tz
tz = _load_tz()
_TLS.tz = tz
return tz
def clear_tz_cache():
"""Force reload timezone on next call. Call after timezone config changes."""
"""Force reload timezone on next call (called after timezone config changes)."""
try:
from flask import current_app
current_app.config.pop("_timezone_cache", None)
from flask import g
g.__dict__.pop("_tz_cache", None)
except Exception:
pass
try:
del _TLS.tz
except AttributeError:
pass
def set_tz(tz_str: str) -> bool: