Dispatcharr/dispatcharr
nagelm 49189465d2 fix(db): pin session timezone to UTC via libpq startup packet
DB sessions were never actually pinned to UTC. Three stacked failures:

1. The connection_created receiver (_force_utc0 in core/apps.py) was a
   nested closure connected with Django's default weak reference. It was
   garbage-collected as soon as CoreConfig.ready() returned, so
   SET TIME ZONE 'UTC0' fired into a dead weakref and never executed -
   in any version since it landed (verified: the signal's receivers list
   on a live 0.27.2 shows a dead weakref).

2. Sessions were UTC on older stacks anyway because Django's own
   init_connection_state configured the timezone. Since native
   psycopg-pool support (Django 5.1+), that path is gated on
   `not self.pool` - and the geventpool mixin's `pool` property is
   always truthy, so Django silently skips timezone (and role)
   configuration for this backend on every connection. This is what
   actually regressed at the psycopg2->3 / Django upgrade: the masking
   layer disappeared, not the (already dead) signal.

3. psycopg3 logs "unknown PostgreSQL timezone: 'UTC0'" because the
   POSIX spec is unresolvable in Python zoneinfo (cosmetic, but it
   means 'UTC0' buys nothing on psycopg3).

Net effect: every session ran at the server-default timezone (verified
live: SHOW TimeZone through the pool returns 'Etc/UTC'). Deployments
whose Postgres default is non-UTC (e.g. /etc/localtime bind-mounts,
the original issue 651 report) get the EPG offset corruption back.

Fix: pin the GUC in the libpq startup packet in the pool backend's
get_connection_params():

- covers every connection the gevent pool creates; no signal, GC, or
  Django-flow dependency (same pattern as the client_encoding pin the
  pool already applies)
- startup-packet GUCs are the session default: they survive ROLLBACK,
  and RESET TimeZone returns to UTC rather than the server default
- 'UTC' resolves cleanly in psycopg3's zoneinfo lookup, and PostgreSQL
  resolves it against its own bundled tzdata, not host-mounted files

The dead signal is removed. Regression tests exercise the session
timezone through the real pool backend explicitly (the test runner's
default engine is the vanilla Django backend), and assert the
RESET-returns-to-UTC session-default property.

Tested: live 0.27.2 AIO (psycopg 3.3.4, PostgreSQL 17) - unpatched
backend+pool session shows 'Etc/UTC'; patched shows 'UTC' incl. after
RESET and ROLLBACK. Full A/B against the dev image with the server
default forced to Europe/Zurich: stock renders a 12:00Z source
programme as start="20260715140000 +0000" in /output/epg; patched
renders it correctly; the regression tests fail on stock and pass
when patched.
2026-07-15 10:20:38 +12:00
..
db fix(db): pin session timezone to UTC via libpq startup packet 2026-07-15 10:20:38 +12:00
__init__.py refactor: Eagerly import celery_app to ensure proper configuration in production 2026-07-08 13:11:17 +00:00
admin.py Alpha v3 2025-02-21 15:31:59 -06:00
app_initialization.py Enhancement: Refactored app initialization to prevent redundant execution across multiple worker processes. Created dispatcharr.app_initialization utility module with should_skip_initialization() function that prevents custom initialization tasks (backup scheduler sync, developer notifications sync) from running during management commands, in worker processes, or in development servers. This significantly reduces startup overhead in multi-worker deployments (e.g., uWSGI with 10 workers now syncs the scheduler once instead of 10 times). Applied to both CoreConfig and BackupsConfig apps. 2026-02-05 13:48:01 -06:00
asgi.py websockets behind auth, cleaned up errors and bad state handling in websocket.jsx 2025-05-10 08:40:53 -04:00
celery.py refactor(celery): Replace worker_ready signal with worker_process_init for plugin discovery and close inherited DB connections. Use djangos PostgrSQL backend for celery tasks. 2026-07-02 13:33:04 -05:00
consumers.py feat(websocket): enhance connection handling with cached IP lookup 2026-05-31 19:28:19 -05:00
gevent_patch.py Enhancement: Update psycopg2 to 3. 2026-05-28 19:27:52 -05:00
jwt_ws_auth.py Enhance JWT authentication error handling and user redirection on token issues. 2025-05-15 14:12:31 -05:00
persistent_lock.py merged in dev 2026-03-26 12:17:43 -04:00
routing.py Websockets, fixed channel name collision, added back in multi-stream per channel support 2025-03-05 17:04:43 -05:00
settings.py refactor(celery): Replace worker_ready signal with worker_process_init for plugin discovery and close inherited DB connections. Use djangos PostgrSQL backend for celery tasks. 2026-07-02 13:33:04 -05:00
settings_test.py test: Add custom test runner for improved test execution in settings_test.py 2026-07-07 18:58:18 +00:00
test_discovery.py refactor: Replace custom test label iteration with centralized test discovery functions for improved maintainability and consistency in test suite management. 2026-07-07 21:30:20 +00:00
test_runner.py refactor: Replace custom test label iteration with centralized test discovery functions for improved maintainability and consistency in test suite management. 2026-07-07 21:30:20 +00:00
urls.py refactor(models): Simplify comments for catch-up fields in Stream and Channel models, clarifying their population process. Update migration docstring for consistency. Remove outdated comments in ProxyServer and URLs related to timeshift handling. 2026-06-26 12:39:54 -05:00
utils.py refactor: Clean up get_client_ip() function in utils.py 2026-05-09 09:48:53 -05:00
wsgi.py Revert "fix(wsgi): ensure gevent monkey-patching before Django import chain" 2026-03-03 15:33:55 -06:00