Files
BanGUI/Docs/adr/ADR-004-APScheduler-Background-Scheduler.md
Lukas 5f0ab40816 refactor(backend): clean up models setup, improve ip utils, add adr docs
- Extract ADR documents for architectural decisions (SQLite, FastAPI, React, APScheduler, Scheduler)
- Refactor setup.py: improve code structure and readability
- Add IP validation utilities with test coverage
- Update frontend components (BanTable, HistoryPage)
- Add pre-commit hooks and CONTRIBUTING.md
- Add .editorconfig for consistent coding standards
2026-05-03 18:04:45 +02:00

2.2 KiB

ADR-004: APScheduler over Celery

Status

Accepted

Context

BanGUI requires background task scheduling for periodic work: geo cache flush, session cleanup, history sync, and blocklist imports.

Decision

Use APScheduler 4.x (AsyncIOScheduler) for background scheduling.

Rationale

Why APScheduler over Celery?

  • No infrastructure: Celery requires a message broker (Redis or RabbitMQ). APScheduler runs in-process with no broker. Given BanGUI's single-instance constraint, a message queue adds unnecessary operational complexity.
  • Async-native: AsyncIOScheduler integrates directly with the asyncio event loop. All BanGUI's I/O (database, HTTP, fail2ban socket) is async. APScheduler jobs are async def functions that await without blocking.
  • Simplicity: BanGUI's job set is fixed and small. Celery's rich task routing, retry policies, and distributed execution are overkill. APScheduler covers cron-style scheduling with simpler semantics.
  • Single-instance enforcement: APScheduler's in-memory job store is a natural fit when there is only one scheduler. No distributed coordination needed.

Why not Celery?

  • Celery's architecture (broker + workers + result backend) is designed for distributed systems. BanGUI is explicitly single-instance.
  • Celery tasks are synchronous wrappers around async code without careful handling. Native async def tasks require async_task() or explicit run_sync, creating friction in an async-first codebase.
  • Added operational burden: Redis or RabbitMQ must be available at startup.

Trade-offs

  • No horizontal scaling of workers: APScheduler jobs run in the single uvicorn worker process. CPU-intensive jobs would block the event loop. (This is not a concern for BanGUI's I/O-bound jobs.)
  • No built-in retry mechanism: Failed jobs must re-raise exceptions or implement retry logic manually. This is acceptable given BanGUI's job idempotency guarantees.

Consequences

  • Scheduler is configured in app/startup.py using AsyncIOScheduler.
  • Jobs live in app/tasks/.
  • Single-worker constraint is enforced via BANGUI_WORKERS=1 validation and the scheduler_lock database table.