-- Kennzahlen und Verlaufsreihen (Phase 13). -- -- Der Plan verlangt zwoelf Diagramme. Zehn davon lassen sich aus Tabellen -- bilden, die es bereits gibt: backup_job_runs traegt Dauer, Durchsatz und -- Datenmengen jedes Laufs, backups die Groessen fuer Deduplizierung und -- Kompression, restore_jobs und verification_jobs ihre Ergebnisse. -- -- Diese Zahlen ein zweites Mal als Zeitreihe abzulegen waere die uebliche -- Loesung und die schlechtere: Zwei Quellen fuer dieselbe Aussage laufen -- auseinander, und man merkt es erst, wenn jemand nachrechnet. Aggregiert wird -- deshalb aus den Ereignistabellen. -- -- Diese Tabelle nimmt nur auf, was sonst **verloren geht**: Momentaufnahmen, die -- beim naechsten Schreiben ueberschrieben werden. repositories.used_bytes ist -- genau das — der heutige Stand, ohne jede Erinnerung an gestern. CREATE TABLE metric_samples ( -- id ist der fortlaufende Bezeichner. -- -- Eine Zeitreihe wird ausschliesslich angehaengt und in Zeitfenstern -- gelesen; eine UUID braeuchte hier mehr Platz und brächte nichts. id BIGSERIAL PRIMARY KEY, -- metric_name benennt die Reihe. metric_name TEXT NOT NULL, -- subject_type benennt die Art des Gegenstands, etwa 'repository'. subject_type TEXT NOT NULL, -- subject_id ist der Gegenstand der Messung. subject_id UUID NOT NULL, -- value ist der gemessene Wert. -- -- DOUBLE PRECISION statt BIGINT: Die Reihe nimmt auch Verhaeltnisse und -- Prozentwerte auf, und ein zweiter Wertetyp je Reihe waere eine Fallgrube. value DOUBLE PRECISION NOT NULL, -- unit ist die Einheit des Werts. -- -- Sie steht an der Messung und nicht in einer Tabelle daneben: Wer eine -- Reihe liest, soll nicht raten muessen, ob 400 Bytes, Kilobytes oder -- Prozent meint. unit TEXT NOT NULL, -- measured_at ist der Zeitpunkt der Messung in UTC. measured_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- NaN und Unendlich werden abgelehnt. -- -- Ein einziger solcher Wert verseucht jede Summe und jeden Durchschnitt der -- Reihe — das Ergebnis ist danach selbst NaN, ohne dass irgendwo ein Fehler -- auftaucht. Aus einer geteilten Null wird so eine Kurve, die nichts mehr -- aussagt. -- -- Die Pruefung ist ausdruecklich `value <> 'NaN'` und **nicht** das uebliche -- `value = value`: PostgreSQL wertet `NaN = NaN` als wahr, anders als -- IEEE 754. Der Standardtrick greift hier also nicht — real geprueft. CONSTRAINT metric_samples_value_finite CHECK ( value <> 'NaN'::float8 AND value <> 'Infinity'::float8 AND value <> '-Infinity'::float8 ) ); -- Der Zugriff erfolgt immer als „diese Reihe, dieser Gegenstand, dieses -- Zeitfenster". Der Index bildet genau das ab. CREATE INDEX metric_samples_series_idx ON metric_samples (metric_name, subject_id, measured_at DESC); -- Fuer das Aufraeumen alter Messungen. CREATE INDEX metric_samples_age_idx ON metric_samples (measured_at); -- Zwei Messungen derselben Reihe zum selben Zeitpunkt sind eine Doppelung. Sie -- entstuende, wenn zwei Control-Server gleichzeitig sammeln — und verfaelschte -- jede Summe. CREATE UNIQUE INDEX metric_samples_unique_idx ON metric_samples (metric_name, subject_id, measured_at); -- --------------------------------------------------------------------------- -- Repositories: Zeitpunkt der letzten Erfassung -- --------------------------------------------------------------------------- ALTER TABLE repositories -- metrics_collected_at ist der Zeitpunkt der letzten Kennzahlenerfassung. -- -- Getrennt von last_checked_at: Das eine ist eine Zustandspruefung, das -- andere eine Messung. Ein Repository kann erreichbar sein, ohne dass -- jemand seinen Fuellstand notiert hat. ADD COLUMN metrics_collected_at TIMESTAMPTZ;