trading-system-docs/notes/trading/system-docs/modul-15-monitoring-control.md

151 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
type: Trading-Modul-System
_organized: true
---
# Modul-15: Monitoring-Control
**Status: FREIGEGEBEN** (20.08.2026) · Container `Modul-15-Monitoring-Control` · Image `monitoring-control:0.1.0`
## Zweck
Zentrale **technische Überwachung und Sicherheits-/Kontrollebene** des Modul-Trading-Systems.
Überwacht die Dienste M03M14 (Health/Readiness/Erreichbarkeit) plus RabbitMQ-Queues/Consumer
und PostgreSQL und leitet daraus deterministisch einen **globalen Trading-Status**
(`ENABLED` / `PAUSED` / `HALTED` / `UNKNOWN`) ab.
**Restriktionen (Nutzer-Vorgabe):**
- **Keine KI/ML**, **keine Trading-Entscheidungen**, **keine Brokerorders**, **keine Shell-/Docker-Control-Rechte**.
- **FAIL-CLOSED:** Bei unklarem/kritischem Zustand wird Trading sicher blockiert (`HALTED`).
- M15 unerreichbar → M09 behandelt den Zustand als `HALTED` (FAIL-CLOSED, siehe M15→M09-Anbindung, FREIGEGEBEN).
- **Keine Event-Schleifen:** Events nur bei Status-Änderung, dedupliziert.
- Container wird **nicht** automatisch neu gestartet/gekillt — M15 beobachtet und entscheidet Status.
- Port 55015 **nur Docker-intern** (`expose`, kein Host-Port).
## Architektur / Datenfluss
```
M03M14 (health/ready) RabbitMQ (Queues) PostgreSQL
│ │ │
└──────────┬────────────┴──────────────┘
Modul-15-Monitoring-Control
├── probe/ MonitoringProbe (HTTP-health + RMQ passive declare + PG)
├── rules/ MonitoringRules (deterministisch, FAIL-CLOSED, versioniert)
├── core/ MonitorService (Status-Engine, Single Source of Truth)
├── storage/ Persistenz (PostgreSQL: system_health/service_health/control_event)
├── publisher/ ControlPublisher (market.control, nur bei Status-Änderung)
└── api/main.py REST-API (intern, Port 55015)
```
Status → `system_health` (append), Services → `service_health` (upsert), Änderungen → `control_event` (append-only Audit).
## API (Docker-intern, Port 55015, kein öffentliches Port-Mapping)
```
GET /health liveness
GET /health/ready readiness (postgresql)
GET /monitoring/status aktueller System-Status + Trading-Status (internes State-Modell)
GET /monitoring/services aktueller Zustand aller überwachten Services/Queues
GET /control/status Control-Status (manual_override, control_api_enabled)
GET /control/trading-state autoritativer Trading-Status für M09
POST /control manuelle Control PAUSE/HALT/RESUME (nur wenn CONTROL_API_ENABLED=true)
```
## Deterministische Regeln (Rules-Engine, Version 0.1.0)
Präzedenz strikt von oben nach unten (FAIL-CLOSED):
1. **UNKNOWN** — PostgreSQL nicht erreichbar → nicht verlässlich → `HALTED`.
2. **UNHEALTHY/HALTED** — kritische Infrastruktur (PG/RabbitMQ) oder Execution-Pfad down.
3. **DEGRADED/PAUSED** — nicht-kritische Dienste down / stale.
4. **HEALTHY/ENABLED** — alles gut.
| Bedingung | System | Trading | Reason-Code |
|---|---|---|---|
| PostgreSQL nicht erreichbar | UNHEALTHY | HALTED | `POSTGRESQL_UNREACHABLE` (+ `PERSISTENCE_FAILURE`) |
| RabbitMQ nicht erreichbar | UNHEALTHY | HALTED | `RABBITMQ_UNREACHABLE` |
| Kritischer Service down (M03M09) | UNHEALTHY | HALTED | `CRITICAL_SERVICE_DOWN:<id>` |
| Market Data stale (> max_age) | UNHEALTHY | HALTED | `MARKET_DATA_STALE` |
| Kritische Queue Consumer fehlt | — | HALTED | `NO_CONSUMER_<queue>` |
| Kritischer Queue-Backlog ≥1000 | — | HALTED | `QUEUE_BACKLOG_CRIT:<queue>:<n>` |
| Nicht-kritische Unhealthy (Analytics/Notification) | DEGRADED | PAUSED | `CRITICAL_SERVICE_DOWN`/`NO_CONSUMER_…` |
| Queue-Backlog ≥100 / Warning | DEGRADED | PAUSED | `QUEUE_BACKLOG:<queue>:<n>` |
| Alles healthy | HEALTHY | ENABLED | — |
**FAIL-CLOSED-Hinweis:** `UNKNOWN` Trading-Status wird in der API auf `HALTED` gemappt (nie ENABLED aus unklarem Zustand).
## Events (market.control Exchange, keine Event-Schleife)
Events werden **nur bei Status-Änderung** publiziert (Idempotenz) — nie bei jedem Poll:
| Event-Typ | Routing-Key | Anlass |
|---|---|---|
| `system.degraded` | `system.degraded` | System → DEGRADED |
| `system.halted` | `system.halted` | System → UNHEALTHY (HALT) |
| `system.recovered` | `system.recovered` | System → HEALTHY |
| `trading.state` | `trading.state` | Trading-Status-Änderung |
| `service.unhealthy` | `service.unhealthy` | Einzelner Service UNHEALTHY |
## Persistenz / Audit (PostgreSQL Modul-01)
- `system_health` — append-only Zeile pro Beobachtung (aktueller Status + reason_codes).
- `service_health` — Upsert pro `service_id` (aktueller Zustand jedes Services/Queue).
- `control_event`**append-only Audit-Trail** jeder Status-/Trading-Änderung (`STATE_CHANGE`) und manuellen Control-Aktion (`MANUAL_*`). Idempotent — eine Änderung wird **genau einmal** auditiert.
## Manuelle Control (PAUSE/HALT/RESUME)
- **Nur wenn `CONTROL_API_ENABLED=true`** (Default `false` → manuelle Control über API deaktiviert, nur Regel-getrieben).
- **RESUME wird verweigert** (`RESUME_BLOCKED_CRITICAL_CAUSE`), solange eine kritische Ursache besteht.
- Jede manuelle Aktion wird append-only in `control_event` auditiert.
## Sicherheit
- **Keine Secrets in DB/Logs/Doku/Commits** — Zugangsdaten ausschließlich via Environment (Compose).
- **Kein Docker-Socket**, **keine Shell-/Docker-Control-Rechte** (CMD=`python main.py`).
- Port 55015 nur intern (`expose`), `restart: unless-stopped`, non-root (uid 1001).
- Env: `POSTGRES_*`, `RABBITMQ_*`, `CONTROL_API_ENABLED`.
## Compose / Betrieb
- Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL`, RabbitMQ `Modul-02-RabbitMQ`.
- Env per Compose, Zugangsdaten nur via ENV (`POSTGRES_*`, `RABBITMQ_*`), nie hart kodiert.
## Unit-Tests
- `tests/test_monitoring.py`: **19/19 grün** — deterministische Rules, FAIL-CLOSED (PG/RabbitMQ/Critical), stale, Queue-Consumer, Idempotenz, Audit genau einmal, Recovery, Reconnect, keine Secrets, `state_version`-Inkrementierung.
## E2E-Verifikation (VPS, 20.08.2026) — 17 Szenarien
Alle 17 verifiziert (System-/Trading-Status via Loop, API `/control/trading-state` + `/monitoring/status` konsistent):
1. **Normalzustand:** `HEALTHY/ENABLED` — API, State-Modell, Loop identisch.
2. **PG-Ausfall:** `HALTED`, `POSTGRESQL_UNREACHABLE`+`PERSISTENCE_FAILURE`, Audit genau einmal.
3. **RabbitMQ-Ausfall:** `HALTED`, `RABBITMQ_UNREACHABLE`.
4. **Risk-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-07-risk-manager`+`NO_CONSUMER_risk.input`.
5. **Portfolio-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-08-portfolio-manager`.
6. **Execution-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-09-execution-service`.
7. **Analytics-Ausfall:** `DEGRADED` + `PAUSED` (Trading nicht vollständig kill).
8. **Notification-Ausfall:** `DEGRADED` + `PAUSED`.
9. **Market Data stale:** `HALTED`, `MARKET_DATA_STALE`; Recovery nach frischen Daten.
10. **Kritischer Consumer fehlt:** HALT-Severity korrekt (NO_CONSUMER auf kritischer Queue).
11. **Queue-Backlog:** Warning/HALT gemäß Schwelle (100/1000, Unit-getestet).
12. **Recovery:** Ursache erneut geprüft, kein Auto-RESUME solange Ursache, Status konsistent, kein Event-Spam.
13. **Manuelle Controls:** mit `CONTROL_API_ENABLED=false` sauber abgelehnt (`control_api_disabled`); Trading unangetastet.
14. **Idempotenz/Event-Flut:** stabiler Zustand → keine wiederholten SYSTEM_HALTED/SERVICE_UNHEALTHY (nur 1 Audit je Änderung).
15. **RabbitMQ-Reconnect:** automatisch (alle Recovery-Zyklen), keine Zombies, keine Eventverluste.
16. **Audit:** `control_event`/`system_health`/`service_health` vollständig + zeitlich nachvollziehbar.
17. **Security:** kein öffentlicher Port (PortBindings `{}`), kein Docker-Socket, keine Secrets im Compose/Logs.
## Bekannte Design-Hinweise (für separaten M15→M09-Vorschlag)
- **M03M06 (Market-Data-Pipeline) sind in `CRITICAL_SERVICES`** klassifiziert. Ausfall eines einzelnen Daten-Moduls führt so zu `HALTED` (über `CRITICAL_SERVICE_DOWN`), nicht ausschließlich über `MARKET_DATA_STALE`. Das ist fachlich konservativ aber zu klären (nur Data-Pipeline vs. Execution-Pfad).
- **DEGRADED → `PAUSED`** (nicht `ENABLED`): bei Analytics/Notification-Ausfall wird Trading vorsichtig gestoppt, nicht vollständig killed. Design-Entscheidung, im Vorschlag benannt.
- **PG-Ausfall ⇒ `PERSISTENCE_FAILURE`**: ohne PG kann `control_event`-Audit nicht geschrieben werden (fachlich korrekt FAIL-CLOSED), aber Audit-Lücke im Fehlerfenster.
## Freigabe
- **FREIGEGEBEN** (20.08.2026, Nutzer-Bestätigung). Implementierung + E2E (17 Szenarien) grün.
- M09-Anbindung **umgesetzt** (Variante C, Event-Cache + HTTP-Final-Check) — siehe `modul-15-m09-anbindung-implementierung.md`, FREIGEGEBEN.
## Nächste Module
- **Modul-16 ff.** — noch Platzhalter (alpine), NICHT gestartet.
---
## Geändert
```
Geändert von: Rain Ocampo
Datum: 20.08.2026
Grund: Modul-15-Monitoring-Control dokumentiert — implementiert + E2E-verifiziert (17 Szenarien grün),
Rules/API/Events/Audit/Security dokumentiert, NICHT FREIGEGEBEN.
```
```
Geändert von: Rain Ocampo
Datum: 20.08.2026
Grund: Modul-15-Monitoring-Control auf FREIGEGEBEN gesetzt (Nutzer-Bestätigung 20.08.2026).
M15→M09-Anbindung (Variante C) als umgesetzt/FREIGEGEBEN vermerkt; Unit-Tests 19/19.
```