""" Конфигурация Gunicorn для продакшена. Архитектура: - 1 воркер (workers=1) — обязательно, т.к.: * SQLite не поддерживает несколько независимых процессов без WAL * SSE event bus (events.py) хранит состояние в памяти одного процесса * Notifier (poster.py) инициализируется один раз в памяти воркера - gthread: несколько потоков внутри воркера — позволяет обслуживать одновременно SSE-поток дашборда + вебхуки + API-запросы - post_fork: стартует Telegram-бот и шедулер внутри воркера (не в мастере) Локальная разработка: python intradbot.py (Flask dev server) Продакшен (Docker): gunicorn -c gunicorn.conf.py "web.server:create_app()" """ import os import threading import time # ── Сервер ──────────────────────────────────────────────────────────────────── bind = f"0.0.0.0:{os.environ.get('PORT', '8080')}" workers = 1 # строго 1 — см. комментарий выше worker_class = "gthread" # многопоточный воркер для SSE + вебхуков одновременно threads = 4 # потоков: SSE-клиенты + API-запросы + запас timeout = 120 # достаточно для долгих SSE-соединений keepalive = 5 # ── Логирование ─────────────────────────────────────────────────────────────── accesslog = "-" # stdout errorlog = "-" # stderr loglevel = "info" access_log_format = '%(h)s "%(r)s" %(s)s %(b)s %(D)sµs' # ── Хуки жизненного цикла ──────────────────────────────────────────────────── def on_starting(server): """Инициализация до форка воркеров (выполняется в мастер-процессе).""" import logging import config import database from intradesk import reference logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[logging.StreamHandler()], ) os.makedirs(os.path.dirname(config.DB_FILE), exist_ok=True) database.init_db() reference.seed_static() reference.load_all() server.log.info("Database and reference data initialized.") def post_fork(server, worker): """ Выполняется в каждом воркер-процессе после fork. Запускает Telegram-бот и шедулер как daemon-потоки внутри воркера. """ import schedule from intradesk import reference from bot.telegram import start_bot threading.Thread(target=start_bot, daemon=True, name="telegram-bot").start() schedule.every().monday.at("10:00").do(reference.load_all) def _run_scheduler(): while True: schedule.run_pending() time.sleep(1) threading.Thread(target=_run_scheduler, daemon=True, name="scheduler").start() worker.log.info("Telegram bot and scheduler started in worker.")