Leistungen

Backend Engineering, Infrastruktur

Branche

B2B-Marktplatz

Jahr

2021-2024

Besucherverfolgung, die ihren eigenen Traffic überlebt.

Ein B2B-Marktplatz brauchte First-Party-Analytics ohne Drittanbieter-Tools. Jeder Seitenaufruf, jede Sitzung, jeder Besucher - serverseitig erfasst. Die erste Implementierung funktionierte bei 50 Requests pro Sekunde. Bei 500 erzeugten gleichzeitige Requests doppelte Besucher, Cookies liefen über, Bots verschmutzten die Daten und die Middleware brachte die Request-Pipeline zum Absturz. Das Tracking musste unsichtbar und unzerstörbar werden.

01.
DIE HERAUSFORDERUNG

Wenn get_or_create sich selbst trifft

Djangos get_or_create ist nicht atomar. Zwei gleichzeitige Requests mit demselben Besucher-Key prüfen beide die Datenbank, finden beide nichts, versuchen beide einzufügen. Einer gelingt. Der andere wirft einen IntegrityError und reißt den gesamten Request mit sich. Bei 500 Requests pro Sekunde passierte das dutzende Male pro Minute. Die Middleware musste Kollisionen ohne Transaktionen, ohne Sperren und ohne jemals einen Fehler an den Nutzer weiterzugeben behandeln.

get_or_create ist sicher in der Dokumentation. Im Produktivbetrieb bei 500 req/s ist es eine Kollisionsfabrik.

02.
DIE LÖSUNG

Das Double-Try-Pattern

Die Lösung ersetzte den einzelnen get_or_create-Aufruf durch eine zweistufige Strategie. Erster Versuch: get_or_create wie gewohnt. Wenn es einen IntegrityError wirft (Duplicate Key), auffangen und auf ein einfaches get() zurückfallen. Das Duplikat wurde von einem gleichzeitigen Request erstellt, der das Rennen gewonnen hat - die Daten existieren, einfach abrufen. Das vermeidet Datenbank-Sperren, vermeidet Transaktions-Overhead und verwandelt einen Absturz in einen 2ms-Retry. Das Pattern bewältigt jedes Niveau an Nebenläufigkeit, weil der Fallback ein einfacher Read ist, kein weiterer Write.

Die Double-Try-Besucherauflösung:

Python
def resolve_visitor(self, request):
    session_key = request.COOKIES.get('visitor_key')

    # Validate cookie format
    if session_key and len(session_key) > 64:
        session_key = None  # discard oversized cookie

    if not session_key:
        session_key = uuid4().hex

    try:
        visitor, created = Visitor.objects.get_or_create(
            session_key=session_key,
            defaults={
                'ip_address': self.get_client_ip(request),
                'user_agent': request.META.get('HTTP_USER_AGENT', '')[:512],
            },
        )
    except IntegrityError:
        # Concurrent request already created it — just fetch
        visitor = Visitor.objects.get(session_key=session_key)

    return visitor, session_key

Kollisionen beobachten

Simulierte gleichzeitige Requests an die Besucher-Middleware. Beobachte, wie IntegrityErrors abgefangen und über das Double-Try-Pattern wiederhergestellt werden.

MIDDLEWARE TRACE
Waiting for requests…
0Requests
0Collisions
0Recovered
0%Collision Rate

Produktions-Grenzfälle

Behandlung übergroßer Cookies

Einige Browser und Proxys kürzen Cookies, die länger als 4KB sind. Die Middleware validiert die Session-Key-Länge vor jedem Datenbank-Lookup. Wenn ein Cookie-Wert das erwartete Format überschreitet oder fehlerhaft ist, wird er stillschweigend verworfen und ein neuer Session-Key generiert. Das verhindert Datenbankabfragen mit Müll-Keys, die nie übereinstimmen würden.

Bot- und Monitoring-Ausschluss

Kompilierte Regex-Muster erkennen gängige Bot-User-Agents (Googlebot, Bingbot, UptimeRobot, Pingdom) und Monitoring-URL-Pfade (/healthcheck, /status). Diese Requests umgehen die Tracking-Middleware vollständig - kein Datenbank-Write, kein Cookie-Set, kein Besucher-Eintrag. Die Regex wird einmal beim Import kompiliert, nicht pro Request.

IP-Adressen-Normalisierung

Requests kommen über Load Balancer mit X-Forwarded-For-Headern, die mehrere IPs, IPv6-Adressen oder fehlerhafte Werte enthalten. Die Middleware extrahiert die linkeste nicht-private IP, validiert ihr Format und fällt auf die direkte Verbindungs-IP zurück. Ungültige Einträge werden als 'unknown' gespeichert statt den Request abstürzen zu lassen.

Bot- und Monitoring-Ausschluss mit kompilierten Regex:

Python
BOT_PATTERNS = re.compile(
    r'bot|crawl|spider|slurp|Googlebot|Bingbot|'
    r'UptimeRobot|Pingdom|StatusCake|NewRelic|Datadog',
    re.IGNORECASE,
)

SKIP_PATHS = re.compile(
    r'^/(healthcheck|status|robots\.txt|favicon\.ico)',
)

def should_track(self, request):
    ua = request.META.get('HTTP_USER_AGENT', '')
    if BOT_PATTERNS.search(ua):
        return False
    if SKIP_PATHS.match(request.path):
        return False
    return True
03.
DAS ERGEBNIS

Zero-Downtime-Tracking im Betrieb

Die Double-Try-Middleware verarbeitet 500+ Requests pro Sekunde ohne IntegrityError-Abstürze. Bot-Ausschluss reduzierte Datenbank-Writes um 34%. Cookie-Validierung eliminierte alle Abfragen mit fehlerhaften Keys. Die Middleware fügt unter 3ms zur Request-Latenz bei p99 hinzu. Über 18 Monate verfolgte sie 2,8 Millionen einzigartige Besucher und 47 Millionen Seitenaufrufe ohne einen einzigen Middleware-bezogenen Fehler in den Logs.

WICHTIGE KENNZAHLEN

0+Requests/sek
0,8MEinzigartige Besucher
0Middleware-Fehler
WAS DER KUNDE SAGT

"Wir haben Google Analytics durch etwas ersetzt, das uns wirklich gehört. Null Daten verlassen unsere Server, null Datenschutz-Banner nötig, und wir vertrauen den Zahlen, weil wir den Code lesen können."

CTO

B2B-Marktplatz · Engineering

FAQ

Warum nicht SELECT FOR UPDATE verwenden?

Warum Cookie-basiertes Tracking statt Django-Sessions?

Wie wird die Bot-Erkennung gepflegt?

TECHNOLOGIE-STACK