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.
APIAuthentifizierte REST direkt im Root (/organizations/…). 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
/organizations/…, /sites/…Authentifizierte REST-API.Cookie oder Token
/mcpMCP-Server für den eigenen KI-Client des Kunden.OAuth-Token
/openapi.jsonOpenAPI-Dokument der v1-API.Public

Die API liegt im Root. Jeder Pfad antwortet zusätzlich mit führendem /v1, einem dauerhaften Alias für die URLs aus der Zeit vor dem Wegfall des Präfixes. Bestehende Integrationen müssen also nichts umstellen, neuer Code lässt es weg.

Die beiden öffentlichen Ingest-Roots sind bewusst kurz: Das hält 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