Kontrolle und Grenzen
Der Grund für die Existenz des Gateways ist nicht «Öffnen», sondern «Öffnen, ohne sich zu verletzen». Diese Seite dokumentiert, was blockiert, was begrenzt und was protokolliert wird – mit den konkreten Werten.
Anweisungen, die das Schema ändern, werden blockiert
Wenn das erste Token eines der folgenden sechs ist, wird mit 400 QUERY_SYNTAX beantwortet.
DROP · TRUNCATE · ALTER · CREATE · GRANT · REVOKE
{"result":"ERROR","error_code":"QUERY_SYNTAX",
"message":"DDL statements are not allowed: DROP"}
Eine Umgehung über Kommentare ist nicht möglich. Block- und Zeilenkommentare werden zuerst entfernt, dann wird das erste Token geprüft.
/* 안녕 */ DROP TABLE pp.mm_tag -- 막힙니다
-- 한 줄
DROP TABLE pp.mm_tag -- 막힙니다
Wenn nach dem Entfernen der Kommentare nichts übrig bleibt, ist das ebenfalls ein 400.
Bei Semikolon wird abgelehnt
{"result":"ERROR","error_code":"QUERY_SYNTAX",
"message":"Multiple statements are not allowed"}
Die Prüfung des ersten Tokens funktioniert nur, wenn ein Request genau einen Satz enthält. Würde SELECT 1; DROP TABLE x erlaubt, wäre das erste Token SELECT und die Prüfung würde unverändert durchlaufen. Deshalb wird bei einem Semikolon vor der inhaltlichen Prüfung abgelehnt.
Obergrenze für Ergebnismengen
| Einstellung | Standardwert |
|---|---|
data.gateway.query.max.rows | 100.000 (0 = unbegrenzt) |
Wenn die Obergrenze erreicht ist, bricht das Gateway das Lesen genau dort ab – es liest nicht das gesamte Ergebnis und schneidet dann ab. So entsteht auch bei riesigen Ergebnissen kein unendlicher Stream.
POST /api/v1/query→truncated-Feld der AntwortPOST /api/v1/query/stream→end-Feld dertruncated-Zeile
Auch das Serverprotokoll erfasst dies.
Query result truncated to max_rows=[100000], lang=[PQL]
HTTP ist 200 und die Zeilen sind normal. Ohne truncated zu prüfen, hält man das abgeschnittene Ergebnis für vollständig und aggregiert darauf aufbauend. Genau deshalb wird dieses Flag immer mitgeliefert.
Ratenbegrenzung
| Einstellung | Standardwert | Was |
|---|---|---|
data.gateway.rate.limit | 1000 | Requests pro Sekunde und Client |
data.gateway.rate.limit.global | 0 (unbegrenzt) | Obergrenze über alle Clients summiert |
data.gateway.trusted.proxies | leer | Liste der direkten Peers, denen X-Forwarded-For vertraut wird |
Der Bucket-Schlüssel ist die vertrauenswürdige Client-IP.
X-Forwarded-For wird standardmäßig ignoriertNur wenn ein direkter Peer in trusted.proxies eingetragen ist, wird der erste Hop des XFF als Client-IP vertraut. Ist die Liste leer, wird immer die tatsächliche Verbindungsadresse verwendet – sonst kann man durch einen erfundenen Header die Grenze umgehen.
Wenn Sie hinter einem Reverse-Proxy stehen, müssen Sie dessen Adresse zwingend in diese Liste eintragen. Andernfalls landen alle Requests im Bucket dieses einen Proxys.
Die globale Obergrenze ist ein Schutz gegen «Bucket-Explosion» – egal wie viele verschiedene IPs erzeugt werden, der Gesamtdurchsatz kann diesen Wert nicht überschreiten.
/admin/* «teilt» sich dieselbe Grenze/api/* und /admin/* verwenden denselben Zähler. Würden sie getrennt, verdoppelte sich die globale Obergrenze faktisch und die Last könnte auf zwei Eingangspunkte verteilt werden.
Anfragegröße
| Einstellung | Standardwert |
|---|---|
data.gateway.max.body.bytes | 1.048.576 (1 MB) |
data.gateway.max.params.bytes | 65.536 (64 KB) |
Bei Überschreitung wird frühzeitig mit 413 abgelehnt – abgebrochen wird vor dem Lesen, nicht nach dem Einlesen.
Timeouts und Slow Log
| Einstellung | Standardwert | Was |
|---|---|---|
data.gateway.async.timeout.ms | 60.000 | Timeout für asynchrone Anfragen |
data.gateway.slow.query.ms | 10.000 | Lesevorgänge, die langsamer sind, werden als Warnung protokolliert |
data.gateway.slow.update.ms | 2.000 | Schreibvorgänge, die langsamer sind, werden als Warnung protokolliert |
Requests werden mit virtuellen Threads verarbeitet. Die Obergrenze für gleichzeitige Ausführungen ist data.gateway.async.concurrency.limit und der Standardwert ist 256 (bei 0 oder weniger unbegrenzt). Wird die Grenze erreicht, wartet die einreichende Seite, bis ein Platz frei wird – nichts wird verworfen, sondern Backpressure ausgeübt. Das Startprotokoll gibt den tatsächlichen Wert aus.
Async executor: virtual threads, concurrency limit=[256]
Der frühere feste Pool erhöhte die Threads erst, wenn die Queue 500 Einträge füllte, und ging nie über 20 – damit war die effektive Gleichzeitigkeit bei 20. Requests, die in der Queue warteten, liefen in den 60-Sekunden-Timeout und erzeugten 503. Der eigentliche Engpass ist der DB-Verbindungspool, deshalb wird jetzt nicht die Poolgröße getunt, sondern nur eine Obergrenze für die Parallelität gesetzt.
Abfrageprotokoll – jeder Request wird erfasst
| Posten | Wert |
|---|---|
| Tabelle | tm_data_gateway_log (Aster TSDB, timeuuid PK) |
| Was bleibt | Sprache · Abfragetext · Zeilenanzahl · Ausführungszeit · Erfolg/Fehler · Fehlermeldung |
| Schreiben | wird in eine Queue eingereiht und in Batches geflusht – der Abfragepfad wird nicht blockiert |
Den Zustand der Queue meldet der gateway.querylog.*-Gauge von GET /admin/metrics.
/api/health ist Readiness, nicht LivenessAllein zu melden, dass der Kontext lebt, ist sinnlos – UP ist nur, wenn die Bereitschaft zur Annahme von Abfragen besteht. Der Prüfsatz (database · inmemory · query_log) muss vollständig wahr sein, sonst wird STARTING oder DEGRADED gemeldet. Ein Gateway, dessen Abfrageprotokoll nicht initialisiert ist, ist nicht UP – wenn es ohne Audit-Protokoll als «normal» erscheint, lässt sich später nicht beantworten, «wer was abgefragt hat».
Authentifizierung · Konsolenkonten
- Die Authentifizierung legt eine einzige Spring-Security-Kette fest. Pfade, die nicht in der Liste stehen, erhalten 401 und sind nicht anonym.
- Es werden weder Sitzungen noch Cookies erzeugt (STATELESS). Die Zugangsdaten kommen bei jedem Request im Header.
- Das Konsolen-Login (
data.gateway.web.user·data.gateway.web.password) öffnet sich nur, wenn beide Felder gefüllt sind. Fehlt eines, meldet der Bildschirm «nicht konfiguriert» und alles wird mit 503LOGIN_NOT_CONFIGUREDabgelehnt.
Würde ein Standardkonto eingebaut, gingen bei jeder Kundeninstallation dieselben Zugangsdaten mit, die niemand ändert. Tragen Sie den Wert in die gerenderte Konfiguration ein (Vorlage in plantpulse-datalake-cli).
Benutzername und Passwort werden immer beide verglichen (auch wenn eine Seite falsch ist, wird die andere nicht übersprungen), und die Fehlerantwort verrät nicht, welche Seite falsch war.
Werte ändern
Die verbindliche Quelle ist config/templates/ in plantpulse-datalake-cli. Änderungen im Container gehen bei einem Neustart verloren.
docker exec plantpulse-datalake pd config diff --templates
docker exec plantpulse-datalake pd restart data-gateway
Das Vorgehen ist in Konfiguration ändern beschrieben, die Schlüsselrotation in Passwort · API-Schlüssel ändern.