Zum Hauptinhalt springen

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 Annahme von genau «einem» Satz ist die Grundlage der DDL-Sperre

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​

EinstellungStandardwert
data.gateway.query.max.rows100.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 Antwort
  • POST /api/v1/query/stream → end-Feld der truncated-Zeile

Auch das Serverprotokoll erfasst dies.

Query result truncated to max_rows=[100000], lang=[PQL]
Abgeschnittene Ergebnisse werden als «Erfolg» zurückgegeben

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​

EinstellungStandardwertWas
data.gateway.rate.limit1000Requests pro Sekunde und Client
data.gateway.rate.limit.global0 (unbegrenzt)Obergrenze über alle Clients summiert
data.gateway.trusted.proxiesleerListe 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 ignoriert

Nur 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​

EinstellungStandardwert
data.gateway.max.body.bytes1.048.576 (1 MB)
data.gateway.max.params.bytes65.536 (64 KB)

Bei Überschreitung wird frühzeitig mit 413 abgelehnt – abgebrochen wird vor dem Lesen, nicht nach dem Einlesen.

Timeouts und Slow Log​

EinstellungStandardwertWas
data.gateway.async.timeout.ms60.000Timeout für asynchrone Anfragen
data.gateway.slow.query.ms10.000Lesevorgänge, die langsamer sind, werden als Warnung protokolliert
data.gateway.slow.update.ms2.000Schreibvorgä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]
Warum auf virtuelle Threads umgestellt wurde

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​

PostenWert
Tabelletm_data_gateway_log (Aster TSDB, timeuuid PK)
Was bleibtSprache · Abfragetext · Zeilenanzahl · Ausführungszeit · Erfolg/Fehler · Fehlermeldung
Schreibenwird 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 Liveness

Allein 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 503 LOGIN_NOT_CONFIGURED abgelehnt.
Es werden keine Standardkonten hinterlegt – das ist der beabsichtigte Standardwert

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.

Verwandte Dokumentation​