No description
  • Python 98.4%
  • Mako 1.6%
Find a file
2025-12-11 16:58:27 +01:00
app Alembic Baseline 2025-12-11 16:54:45 +01:00
migrations Alembic Baseline 2025-12-11 16:54:45 +01:00
.gitignore Python repository created 2025-12-11 12:06:33 +01:00
alembic.ini Alembic Baseline 2025-12-11 16:54:45 +01:00
docker-compose.yml Initial Source code version 2025-12-11 12:07:17 +01:00
poetry.lock Alembic Baseline 2025-12-11 16:54:45 +01:00
pyproject.toml Alembic Baseline 2025-12-11 16:54:45 +01:00
README.md README update 2025-12-11 16:58:27 +01:00


Task Queue System (FastAPI + PostgreSQL + RabbitMQ) v0.3

Produktionsreifes Beispielsystem für Job/TaskVerarbeitung mit Python3.12, FastAPI, SQLAlchemy (async), PostgreSQL, RabbitMQ (aiopika), argparseCLI und migrationsfähiger DBStruktur via Alembic.

Inhalt

  • Features
  • Architektur
  • Voraussetzungen
  • Schnellstart (lokal)
  • Konfiguration
  • API
  • CLI
  • Worker
  • Alembic (Migrationen)
  • Troubleshooting
  • Nächste Schritte

Features

  • JobType → Tasks (z.B. MailCheck):
    MXValidation, SPFValidation, TLSCheck, CertValidity, MTASTSCheck, TLSRPTCheck
  • TaskAbhängigkeiten (DAG):
    TLSCheck & CertValidity warten auf MXValidation; andere Tasks laufen unabhängig.
  • Persistenz: Jobs/Tasks in PostgreSQL (UUID, JSONB, Enums, Timestamps).
  • RabbitMQ (TopicExchange): Routing per Key <JobType>.<TaskName>.
  • Worker (async) mit Retries & ExponentialBackoff; StatusUpdates in DB.
  • API (FastAPI): Job erstellen & Status abfragen.
  • CLI (argparse): create-job und get-job.
  • Alembic: saubere Baseline & künftige Migrationen (async env.py, OnlineModus).

Architektur

Client/CLI → FastAPI → PostgreSQL
                   ↘ RabbitMQ (Topic)
                      ↘ Worker (MX/SPF/TLS/Cert/MTA-STS/TLS-RPT)
  • Job erstellen: API legt Job + Tasks (abhängig vom JobType) an, published Tasks via RabbitMQ.
  • Worker: Konsumieren TaskMessages, führen Checks aus (DNS, SMTP STARTTLS, HTTP), speichern Ergebnisse in DB, releasen abhängige Tasks.
  • Status: JobStatus wird aus TaskStatus aggregiert.

Voraussetzungen

  • Python 3.12
  • PostgreSQL 16 (lokal oder via Docker)
  • RabbitMQ 3 (lokal oder via Docker)
  • Optional: poetry oder uv (Pakete/venv)

Schnellstart (lokal)

1) Services (optional per Docker)

docker-compose up -d
# Ports:
# PostgreSQL → 5432
# RabbitMQ   → 5672 (AMQP), 15672 (UI)

2) Virtuelles Environment & Pakete

python3.12 -m venv .venv
source .venv/bin/activate
pip install -U pip wheel
pip install fastapi uvicorn[standard] sqlalchemy asyncpg pydantic aio-pika typer httpx python-dotenv dnspython alembic

3) Umgebungsvariablen

export DATABASE_URL="postgresql+asyncpg://postgres:postgres@localhost:5432/tasks"
export RABBITMQ_URL="amqp://guest:guest@localhost/"
export RABBITMQ_EXCHANGE="tasks"
export MAX_RETRIES=3
export DNS_TIMEOUT=3.0
export DNS_LIFETIME=5.0
export SMTP_TIMEOUT=10.0
export HTTP_TIMEOUT=8.0

4) Alembic Baseline anwenden

Wichtig: Die App führt kein create_all() mehr aus; Tabellen werden ausschließlich über Alembic erstellt.

# (Bereits vorhandene DB ohne Tabellen)
PYTHONPATH=. alembic upgrade head

5) API starten

uvicorn app.api:app --host 0.0.0.0 --port 8000 --reload

6) Worker starten (jeweils in separaten Terminals)

python -m app.workers.mail_mx_worker
python -m app.workers.mail_spf_worker
python -m app.workers.mail_tls_worker
python -m app.workers.mail_cert_worker
python -m app.workers.mail_mtasts_worker
python -m app.workers.mail_tlsrpt_worker

7) CLI (argparse)

python -m app.cli create-job MailCheck --domain example.com --api-url http://127.0.0.1:8000
python -m app.cli get-job <JOB_ID> --api-url http://127.0.0.1:8000

Konfiguration

  • .env (optional): Du kannst obige Variablen in eine .env legen und z.B. via python-dotenv laden.
  • RHELfreundlich: CLI nutzt argparse statt Typer/Click; keine Abhängigkeit von ClickVersionen.

API

POST /jobs

Body:

{
  "job_type": "MailCheck",
  "domain": "example.com",
  "payload": {}
}

Antwort (verkürzt):

{
  "id": "UUID",
  "job_type": "MailCheck",
  "status": "queued|running|success|failed",
  "domain": "example.com",
  "payload": {},
  "tasks": [
    {"id":"UUID","name":"MXValidation","status":"queued", ...},
    ...
  ]
}

GET /jobs/{job_id}

Gibt Job + TaskDetails (Result/Errors) zurück.


CLI

Job anlegen

python -m app.cli create-job MailCheck --domain example.com --api-url http://127.0.0.1:8000

Job abfragen

python -m app.cli get-job <JOB_ID> --api-url http://127.0.0.1:8000

Tipp: Setze TASK_QUEUE_API_URL="http://127.0.0.1:8000" und spare dir --api-url.


Worker

  • MXValidation: DNS MXLookup (dnspython), sortiert nach Preference.
  • SPFValidation: TXT SPFErkennung, BasisParsing (Mechanismen, allQualifier).
  • TLSCheck: SMTP EHLO → STARTTLS (Port 25); TLSVersion/Cipher/Zertifikat; nutzt MXHosts (aus MXValidation oder Fallback DNS).
  • CertValidity: Zertifikat notBefore/notAfter; Gültigkeit + Tage bis Ablauf.
  • MTASTSCheck: _mta-sts.<domain> TXT + https://mta-sts.<domain>/.well-known/mta-sts.txt.
  • TLSRPTCheck: _smtp._tls.<domain> TXT → rua=mailto:….

Retry/Backoff: Bei Fehlern retries++, Status QUEUED, erneutes Publish mit ExponentialBackoff.


Alembic (Migrationen)

Async env.py

  • Lädt Base.metadata und importiert Modelle (from app import models) wichtig, damit alle Tabellen in metadata.tables stehen.
  • Führt run_migrations() direkt beim Import aus (kein __main__Guard).
  • OnlineModus mit create_async_engine(...) und run_sync(...):
    def do_run_migrations(connection: Connection) -> None:
        context.configure(
            connection=connection,
            target_metadata=Base.metadata,
            compare_type=True,
            compare_server_default=True,
            include_schemas=True,
            process_revision_directives=process_revision_directives, # skip empty
        )
        with context.begin_transaction():
            context.run_migrations()
    await async_conn.run_sync(do_run_migrations)
    

Baseline anwenden

PYTHONPATH=. alembic upgrade head

Neue Revision erzeugen

  1. Modelle ändern (z.B. Spalte ergänzen).
  2. Revision:
PYTHONPATH=. alembic revision --autogenerate -m "add tasks.priority"
  1. Prüfen und anwenden:
PYTHONPATH=. alembic upgrade head

Hinweis (Enums): Beim Erweitern von EnumWerten ggf. manuell ergänzen:

op.execute("ALTER TYPE jobstatus ADD VALUE IF NOT EXISTS 'paused'")

Troubleshooting

Alembic erzeugt leere Revisionen

  • Prüfe, ob env.py beim Import run_migrations() aufruft (kein if __name__ == "__main__").
  • Sicherstellen: context.configure(connection=..., target_metadata=...) wird im OnlinePfad gesetzt.
  • Modelle importieren (from app import models), damit Base.metadata.tables nicht leer ist.
  • Kein OfflineRun (--sql vermeiden); OfflinePfad ggf. bewusst deaktivieren.
  • Starte Alembic aus dem ProjektRoot mit PYTHONPATH=., damit from app ... funktioniert.

MissingGreenlet beim APIZugriff

  • Ursache: LazyLoading einer Beziehung in async Kontext (z.B. job.tasks).
  • Fix: Beziehungen standardmäßig eager laden (lazy="selectin") oder im Query options(selectinload(Job.tasks)) setzen.

Typer/ClickFehler in CLI (RHEL)

  • CLI nutzt argparse (keine Abhängigkeit zu Typer/Click).

Nächste Schritte

  • Makefile/Scripts ergänzen (z.B. make migrate, make revision m="...").
  • Docker Entrypoint: vor AppStart alembic upgrade head.
  • Observability: Logging strukturieren; Metriken/Tracing (optional).
  • Tests: Unit/IntegrationTests (z.B. WorkerProbes mit Mocks, DNS/SMTP/HTTP Timeouts).