Diese Dokumentation entsteht gerade: einzelne Seiten können noch unvollständig oder stellenweise ungenau sein.
fastmon Docs

Architektur

Wie aus einem Beacon ein Dashboard wird. Datenfluss, Stores und was jeder davon tut.

Fastmon besteht aus zwei Services (einem API-Backend und einem Single-Page-Dashboard) und drei Datenstores. Diese Form zu kennen hilft, wenn eine Metrik fehlt, wenn du langsame Ingestion debuggst oder wenn Compliance fragt, wie das Ding funktioniert.

High-Level-Diagramm

                   Browser


             beacon.js  (PerformanceObserver)

              POST /c/{collector_hash}

        ┌─────────────────────────────────────┐
        │  EU-Edge                            │
        │  entfernt IP + User-Agent,          │
        │  leitet Land ab, berechnet stitch   │
        └─────────────────────────────────────┘
                       │  FM-*-Header, keine rohe IP / UA

     Collector ──── validieren, Rate-Limit ────┐
                       │                        │
              Per-Prozess-Buffer (~30 s)        │
                       │                        │
                       ▼                        │
                Analytics-Store                 │
                          Dashboard ────────────┘


                        Analytics-Queries

Drei Stellen in der Pipeline sind wichtig, wenn du das Verhalten verstehen willst:

  • Browser → EU-Edge. Der Edge entfernt IP und User-Agent an der Netzwerkgrenze, leitet aus der IP den zweistelligen Ländercode ab und berechnet den begrenzten, pseudonymen stitch, bevor er weiterreicht. Die Anwendung sieht nur vorbereitete FM-*-Header, nie eine rohe IP oder UA. Siehe Privacy.
  • Edge → Collector. Einige wenige gebündelte POSTs pro Pageview, verteilt über dessen Lebenszyklus (Laden, pagehide / visibilitychange, periodischer Flush). Siehe Der RUM-Beacon.
  • Collector → Analytics-Store. Beacons landen in einem Per-Prozess-Buffer und flushen alle paar Sekunden via gebündeltem Insert. Daher braucht dein erster Besucher ~30 s, bis er im Dashboard auftaucht.

Komponenten

KomponenteWas sie tut
Beacon-ScriptVanilla JS, ~10 KB komprimiert. Beobachtet Web Vitals, batcht, postet.
EU-EdgeEntfernt IP und User-Agent an der Netzwerkgrenze, leitet das Land aus der IP ab, berechnet den begrenzten stitch. Rohe IP und UA erreichen die Anwendung nie.
CollectorÖffentlicher Ingest unter /c/{collector_hash}. Validation, Rate-Limit, serverseitige Sanitisierung.
API/v1/...: authentifizierte REST. Versorgt das Dashboard.
DashboardSingle-Page-App. Liest über die API. Nur Static-Assets.
Config-DBUser, Organisationen, Sites, Einstellungen, Billing.
Analytics-StoreBeacon-Events, aggregierte Metriken, Error-Logs.
Session-StoreSessions, Rate-Limits, kurzlebige Caches.

Backend und Frontend liegen in unabhängigen Repos und deployen unabhängig.

Storage-Schichten

StoreInhaltHinweise
Config-DBUser, Orgs, Sites, Einstellungen, BillingSingle Primary, asynchroner Zugriff.
Analytics-StoreBeacon-Events, Aggregate, Error-LogsColumnar, Queries pro Org begrenzt.
Session-StoreSessions, Rate-Limits, kurzlebige CachesIn-Memory-Store unter EU-Jurisdiktion.

Alle drei werden in der EU betrieben mit versionierten, idempotenten Migrations beim Deploy. Datenresidenz, Subprozessoren und die US-CLOUD-Act-Position stehen unter Privacy → Wo die Daten liegen.

Routing-Layout

PfadZweckAuth
GET /s/{source_hash}.jsÖffentliche Beacon-Script-Auslieferung. Strong-Cache, ETag.Public
POST /c/{collector_hash}Öffentlicher Beacon-Ingest.Public
/v1/...Authentifizierte REST-API.Cookie oder Token
/openapi.jsonOpenAPI-Dokument der v1-API.Public

Die beiden öffentlichen Roots stehen bewusst nicht unter /v1: kurze URLs halten den Beacon-Payload klein, und die Cache-Regeln unterscheiden sich (das Beacon-Script aggressiv gecached; API-Responses typischerweise nicht).

Was wo liegt, kurz

DatenWoRetention
Beacon-Events (raw + roll-ups)Analytics-Storedata_retention_days pro Site (Default 90).
Site-, Org-, Member-ConfigConfig-DBBis zur Löschung.
Aktive Sessions (Auth)Session-StoreGleitendes Fenster, per Default 30 Tage idle.
Rate-Limit-CounterSession-StorePer-Fenster-TTL, < 5 min.
Rohe IP / User-AgentNie persistiertAm EU-Edge vor dem Ingest zerstört. Die gespeicherte Zeile trägt höchstens den begrenzten, pseudonymen stitch. Siehe Privacy.

Warum manches so ist, wie es ist

Warum 30-Sekunden-Buffer beim Ingest?

Weil Inserts pro Zeile in dieser Größenordnung jede Analytics-DB killen würden. Batching in einen Per-Prozess-Buffer, der alle paar Sekunden flusht, ist Standard; der Analytics-Store batcht die Inserts auf seiner Seite noch einmal. Der Preis in Summe: ~30 s Verzögerung, bis ein Beacon in den Queries auftaucht.

Warum source_hash und collector_hash getrennt?

Damit das Script-Template (und sein Cache) sich ändern kann, ohne dass die Daten-Pipeline darunter leidet. Ein Beacon-Upgrade heißt einfach: neues Template ausrollen; die Collector-Endpunkte bleiben gültig. Mehr dazu unter Der RUM-Beacon: Cache und Rollouts.

Warum ein Columnar-Analytics-Store?

Analytics-Queries lesen wenige Spalten (eine Metrik, ein paar Dimensionen) über viele Zeilen. Spaltenweise Speicherung liest nur diese Spalten von der Platte statt jeder Zeile, und Timing-Daten komprimieren in dieser Form sehr gut. Vektorisierte Aggregation pro Spalte macht Perzentile und Group-Bys nochmal deutlich schneller. Auf Postgres ginge das auch, wäre nur spürbar langsamer und teurer pro Query.

Wie aktuell sind die Daten: live vs. aggregiert

Analytics-Queries entscheiden je nach Zeitbereich, woher sie lesen. Alles innerhalb der letzten 6 Stunden geht direkt auf den Roh-Beacon-Stream, also frisch bis auf die ~30 s Ingest-Verzögerung von oben. Längere Bereiche (24 h, 7 d, 30 d, custom) lesen aus stündlich vor-aggregierten Tabellen, die das Backend einmal pro Stunde neu aufbaut. Gleiche Zahlen, Antwort unter 100 ms, und die Scan-Kosten wachsen nicht mit, wenn die Aufbewahrungsdauer steigt.

Für Dashboard und API ändert sich nichts; du merkst nur, dass die Queries schnell sind. Die einzige sichtbare Folge: Die zuletzt abgeschlossene Stunde braucht bis zu eine Stunde, bis sie in der aggregierten Sicht auftaucht. Das 6-h-Live-Fenster deckt diese Lücke ab, also stimmen die Live-Dashboards trotzdem.

Warum ein eigener Session-Store?

Sessions und Rate-Limit-Counter brauchen Reads im Millisekunden-Bereich auf jedem Request, aber keine Langzeit-Durability. In der Config-DB hätten sie nichts zu suchen; sie auszulagern hält den Auth-Pfad schnell und sauber getrennt.

Verwandt

On this page