Version: 1.4.1
Kategorie: 🔍 Analytics
Status: ✅ Implementiert & Erweitert (Phase 2)
- Übersicht
- Features
- Änderungserkennung
- API-Referenz
- Verwendungsbeispiele
- Performance
- Konfiguration
- Best Practices
Die Diff API bietet Git-ähnliche strukturierte Differenzberechnung für ThemisDB's MVCC-System. Sie analysiert Änderungen in der Datenbank zwischen zwei Zeitpunkten und kategorisiert sie in Hinzugefügt, Geändert und Gelöscht-Operationen.
- Diff nach Sequenz: Vergleiche Datenbankzustände zwischen zwei Sequenznummern
- Diff nach Zeitstempel: Vergleiche Datenbankzustände zwischen zwei Zeitstempeln (ISO 8601 oder Millisekunden)
- Diff nach Tag: Vergleiche Datenbankzustände zwischen benannten Snapshots (erfordert Phase 1)
- Filterung: Filtere Ergebnisse nach Tabellenname oder Schlüsselpräfix
- Paginierung: Verarbeite große Ergebnismengen effizient mit Limit und Offset
- Caching: Automatisches Ergebnis-Caching mit 5-Minuten-TTL für verbesserte Performance
- Binäre Such-Optimierung: Schnelle Zeitstempel-zu-Sequenz-Konvertierung
- Audit-Berichte: Erstelle strukturierte Änderungsberichte für Compliance
- Debugging: Verfolge Änderungen zwischen Deployments oder Vorfällen
- Datenmigration: Überprüfe Datenänderungen nach Migrationsoperationen
- Änderungsverfolgung: Überwache spezifische Entities oder Tabellen auf Änderungen
- Compliance: Generiere manipulationssichere Change-Logs für regulatorische Anforderungen
- Diff-Berechnung nach Sequenzbereich
- Diff-Berechnung nach Zeitstempelbereich (optimiert mit binärer Suche)
- Diff-Berechnung nach Tag (mit SnapshotManager-Integration)
- Änderungskategorisierung (Hinzugefügt/Geändert/Gelöscht)
- Filterung nach Tabellenname
- Filterung nach Schlüsselpräfix
- Paginierung (Limit und Offset)
- Werte-Einschluss-Toggle
- Ergebnis-Caching mit TTL
- JSON-Serialisierung
- REST API Endpoints
- Eingabevalidierung für Sicherheit
- Umfassende Testabdeckung (95%+)
Die DiffEngine kategorisiert Änderungen intelligent als ADDED oder MODIFIED basierend auf dem Abfragebereich:
ADDED Erkennung (Neue Schlüssel):
- Bei Abfrage ab Sequenz 0: Alle PUT-Events sind definitiv neue Schlüssel (ADDED)
- Bei Abfrage ab Sequenz > 0: Einzelne PUT-Events werden konservativ als MODIFIED markiert (könnten neu oder bestehend sein)
MODIFIED Erkennung (Aktualisierte Schlüssel):
- Mehrere PUT-Events für denselben Schlüssel im Bereich: Definitiv MODIFIED
- Einzelnes PUT-Event mit from_sequence > 0: Konservativ MODIFIED
DELETED Erkennung:
- Jedes DELETE-Event innerhalb des Bereichs
Best Practice für genaue Erkennung:
# Für genaue ADDED-Erkennung, Abfrage ab Sequenz 0
curl "http://localhost:8765/api/v1/diff?from=0&to=200"
# Für Änderungen seit einem Checkpoint (nur MODIFIED)
curl "http://localhost:8765/api/v1/diff?from=100&to=200"Schlüssel "users:1" Änderungshistorie:
Seq 50: PUT "Alice" → Außerhalb des Abfragebereichs
Seq 100: PUT "Alice v2" → Abfrage beginnt hier
Seq 150: PUT "Alice v3" → Im Bereich
Seq 200: PUT "Alice v4" → Abfrage endet hier
Ergebnis: MODIFIED (mehrere PUTs im Bereich)
- old_value: "Alice v2" (erstes Event im Bereich)
- new_value: "Alice v4" (letztes Event im Bereich)
Berechne strukturierten Diff zwischen zwei Zeitpunkten.
Query-Parameter:
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
from |
string | Ja | Startpunkt (Sequenznummer oder ISO 8601 Zeitstempel) |
to |
string | Ja | Endpunkt (Sequenznummer oder ISO 8601 Zeitstempel) |
table |
string | Nein | Filtere nach Tabellenname |
key_prefix |
string | Nein | Filtere nach Schlüsselpräfix |
include_values |
boolean | Nein | Include tatsächliche Werte (Standard: true) |
limit |
integer | Nein | Maximale Anzahl Änderungen (Standard: 1000, 0 = kein Limit) |
offset |
integer | Nein | Überspringe erste N Änderungen (Standard: 0) |
enable_caching |
boolean | Nein | Aktiviere Ergebnis-Caching (Standard: true) |
Response-Format:
{
"added": [
{
"type": "added",
"key": "users:123",
"new_value": "{\"name\":\"Alice\",\"email\":\"alice@example.com\"}",
"sequence": 150,
"timestamp_ms": 1736657231000,
"metadata": {}
}
],
"modified": [
{
"type": "modified",
"key": "users:456",
"old_value": "{\"name\":\"Bob\",\"email\":\"bob@old.com\"}",
"new_value": "{\"name\":\"Bob\",\"email\":\"bob@new.com\"}",
"sequence": 151,
"timestamp_ms": 1736657232000,
"metadata": {}
}
],
"deleted": [
{
"type": "deleted",
"key": "users:789",
"old_value": "{\"name\":\"Charlie\"}",
"sequence": 152,
"timestamp_ms": 1736657233000,
"metadata": {}
}
],
"stats": {
"added_count": 1,
"modified_count": 1,
"deleted_count": 1,
"total_changes": 3
},
"from_sequence": 100,
"to_sequence": 200
}Rufe Diff-Cache-Statistiken ab.
Response:
{
"cache_size": 15,
"max_cache_size": 100,
"cache_ttl_seconds": 300
}Lösche den Diff-Ergebnis-Cache.
Response:
{
"status": "success",
"message": "Cache cleared successfully"
}Vergleiche Änderungen zwischen Sequenz 100 und 200:
curl "http://localhost:8765/api/v1/diff?from=100&to=200"Vergleiche Änderungen zwischen zwei Daten:
curl "http://localhost:8765/api/v1/diff?from=2026-01-01T00:00:00&to=2026-01-11T23:59:59"curl "http://localhost:8765/api/v1/diff?from=1735689600000&to=1736657231000"curl "http://localhost:8765/api/v1/diff?from=100&to=200&table=users"Erste 50 Änderungen abrufen:
curl "http://localhost:8765/api/v1/diff?from=100&to=200&limit=50&offset=0"Nächste 50 Änderungen abrufen:
curl "http://localhost:8765/api/v1/diff?from=100&to=200&limit=50&offset=50"curl "http://localhost:8765/api/v1/diff?from=100&to=200&include_values=false"curl "http://localhost:8765/api/v1/diff?from=100&to=200&table=users&key_prefix=entity:users:&limit=100"import requests
import json
# Diff berechnen
response = requests.get(
"http://localhost:8765/api/v1/diff",
params={
"from": "100",
"to": "200",
"table": "users",
"limit": 50
}
)
diff_result = response.json()
# Ergebnisse verarbeiten
print(f"Gesamtänderungen: {diff_result['stats']['total_changes']}")
print(f"Hinzugefügt: {diff_result['stats']['added_count']}")
print(f"Geändert: {diff_result['stats']['modified_count']}")
print(f"Gelöscht: {diff_result['stats']['deleted_count']}")
# Geänderte Entities durchgehen
for change in diff_result['modified']:
print(f"Geändert: {change['key']}")
print(f" Alt: {change['old_value']}")
print(f" Neu: {change['new_value']}")import requests
from datetime import datetime, timedelta
# Änderungen der letzten 24 Stunden abrufen
now = datetime.now()
yesterday = now - timedelta(days=1)
response = requests.get(
"http://localhost:8765/api/v1/diff",
params={
"from": yesterday.isoformat(),
"to": now.isoformat(),
"include_values": "true"
}
)
diff = response.json()
# Audit-Bericht generieren
print("=== Täglicher Audit-Bericht ===")
print(f"Zeitraum: {yesterday} bis {now}")
print(f"\nZusammenfassung:")
print(f" Neue Entities: {diff['stats']['added_count']}")
print(f" Aktualisierte Entities: {diff['stats']['modified_count']}")
print(f" Gelöschte Entities: {diff['stats']['deleted_count']}")
# Detaillierte Änderungen
if diff['modified']:
print("\nDetaillierte Änderungen:")
for change in diff['modified']:
print(f" - {change['key']} geändert bei Sequenz {change['sequence']}")Performance-Ziele und tatsächliche Ergebnisse:
| Datengröße | Ziel | Tatsächlich | Hinweise |
|---|---|---|---|
| 100 Änderungen | <10ms | ~5ms | Schnell für kleine Diffs |
| 1K Änderungen | <50ms | ~25ms | Typischer Anwendungsfall |
| 10K Änderungen | <100ms | ~80ms | Ziel erreicht ✅ |
| 100K Änderungen | <1s | ~750ms | Ziel erreicht ✅ |
- Caching nutzen: Aktiviere Caching für häufig angeforderte Diff-Bereiche
- Paginierung: Verwende Limit/Offset für große Ergebnismengen
- Werte deaktivieren: Setze
include_values=falsewenn nur Änderungserkennung benötigt wird - Früh filtern: Verwende Table- und Key_prefix-Filter zur Reduzierung der Verarbeitung
- Zeitstempel vs Sequenz: Sequenz-basierter Diff ist schneller als Zeitstempel-basierter
- Cache TTL: 5 Minuten
- Max Cache-Größe: 100 Einträge
- Automatische LRU-Verdrängung bei vollem Cache
- Cache-Hit verbessert Performance signifikant (~10x schneller)
# config.yaml
features:
enable_diff_api: true # Diff API aktivieren
diff:
cache_ttl_seconds: 300 # Cache TTL (5 Minuten)
max_cache_size: 100 # Maximale gecachte Ergebnisse
default_limit: 1000 # Standard-Paginierungslimit
max_limit: 10000 # Maximal erlaubtes Limit# Diff API aktivieren
export THEMIS_ENABLE_DIFF_API=true
# Cache-Konfiguration
export THEMIS_DIFF_CACHE_TTL=300
export THEMIS_DIFF_MAX_CACHE_SIZE=100# ✅ Gut: Spezifischer Zeitbereich
curl "http://localhost:8765/api/v1/diff?from=2026-01-10T00:00:00&to=2026-01-11T00:00:00"
# ❌ Schlecht: Zu großer Bereich
curl "http://localhost:8765/api/v1/diff?from=0&to=1000000"# ✅ Gut: Nach Tabelle filtern
curl "http://localhost:8765/api/v1/diff?from=100&to=200&table=users"
# ❌ Schlecht: Keine Filterung bei großem Datensatz
curl "http://localhost:8765/api/v1/diff?from=0&to=10000"# ✅ Gut: Paginiert
curl "http://localhost:8765/api/v1/diff?from=100&to=1000&limit=100&offset=0"
# ❌ Schlecht: Kein Limit
curl "http://localhost:8765/api/v1/diff?from=100&to=10000"# ✅ Gut: Schnelle Änderungserkennung
curl "http://localhost:8765/api/v1/diff?from=100&to=200&include_values=false"
# ❌ Schlecht: Unnötiger Datentransfer
curl "http://localhost:8765/api/v1/diff?from=100&to=200&include_values=true"{
"error": "Invalid sequence range: from=200 >= to=100",
"status": 400
}Lösung: Stelle sicher dass from < to
{
"error": "Invalid timestamp format: 'invalid'. Expected milliseconds or ISO 8601 format",
"status": 400
}Lösung: Verwende ISO 8601 (YYYY-MM-DD oder YYYY-MM-DDTHH:MM:SS) oder Millisekunden seit Epoch
Die aktuelle Implementierung kategorisiert Änderungen basierend auf Events innerhalb des Diff-Bereichs. Für PUT-Events:
- Wenn der Schlüssel mehrfach vorkommt: MODIFIED
- Wenn der Schlüssel einmal vorkommt: MODIFIED (konservative Annahme)
Warum: Ohne Abfrage der vollständigen Changefeed-Historie vor from_sequence kann nicht definitiv festgestellt werden, ob ein Schlüssel neu erstellt (ADDED) oder aktualisiert (MODIFIED) wurde.
Workaround: Für genaue ADDED-Events verwende from=0 um die vollständige Historie einzubeziehen, oder frage mit einer from_sequence ab, die vor der Erstellung des Schlüssels liegt.
Zukunft: Eine zukünftige Erweiterung kann ein optionales Flag hinzufügen, um eine aufwändige Historiensuche für genaue Klassifizierung zu ermöglichen.
# Szenario: Schlüssel "users:123" wurde bei Sequenz 50 erstellt
# Abfrage ab Sequenz 100 zeigt ihn nicht als ADDED
# ❌ Ungenau (zeigt als MODIFIED wenn nach Seq 100 geändert)
GET /api/v1/diff?from=100&to=200
# ✅ Genau (zeigt als ADDED bei Sequenz 50)
GET /api/v1/diff?from=0&to=200- MVCC Architektur
- Changefeed Dokumentation
- Named Snapshots
- Point-in-Time Recovery
- Git-ähnliche Features Forschung
Erstellt: 2026-01-12
Zuletzt aktualisiert: 2026-01-12
Version: 1.0
Status: Produktionsreif ✅