Skip to content

DATA_RETENTION_DOWNSAMPLING

GitHub Actions edited this page Jan 2, 2026 · 1 revision

Automatische Datenaufbewahrung und Downsampling

Überblick

Dieses Dokument beschreibt, wie TaskScheduler fΓΌr automatische Datenaufbewahrung (Retention) und Downsampling von Zeitreihendaten verwendet wird. Das System ersetzt hochauflΓΆsende Datenpunkte automatisch durch niedrigauflΓΆsende Aggregate nach konfigurierbaren ZeitrΓ€umen.

Motivation

Problem

  • Speicherexplosion: 1-Sekunden-Datenpunkte verbrauchen enorm viel Speicher
  • Lange Aufbewahrungszeiten: Historische Daten werden fΓΌr Analysen benΓΆtigt
  • UnnΓΆtige GranularitΓ€t: Nach einem Jahr ist 1s-AuflΓΆsung meist nicht mehr nΓΆtig

LΓΆsung

Automatisches Ersetzen von hochauflΓΆsenden Daten durch Aggregate:

  • 1s-Daten nach 1 Jahr β†’ 1h-Aggregate (Mittelwert, Standardabweichung, Min, Max)
  • AuswertungsfΓ€higkeit erhalten: Statistische Eigenschaften bleiben erhalten
  • Speicherreduktion: ~99% Einsparung (3600:1 Ratio fΓΌr 1sβ†’1h)

AnwendungsfΓ€lle

Use Case 1: Einfaches Downsampling (1s β†’ 1h nach 1 Jahr)

Szenario: IoT-Sensoren senden 1s-Daten. Nach 1 Jahr wird die hohe AuflΓΆsung nicht mehr benΓΆtigt.

Implementierung:

ScheduledTask downsample_task;
downsample_task.type = ScheduledTask::TaskType::AQL_QUERY;
downsample_task.aql_query = R"(
    FOR d IN timeseries
    FILTER d.resolution == '1s'
    AND d.timestamp < DATE_SUB(NOW(), 1, 'year')
    COLLECT 
        metric = d.metric,
        entity = d.entity,
        hour = DATE_TRUNC(d.timestamp, 'hour')
    AGGREGATE
        avg_value = AVG(d.value),
        stddev_value = STDDEV(d.value),
        min_value = MIN(d.value),
        max_value = MAX(d.value),
        count = COUNT(d)
    INSERT {
        metric: metric,
        entity: entity,
        timestamp: hour,
        resolution: '1h',
        value: avg_value,
        statistics: {
            avg: avg_value,
            stddev: stddev_value,
            min: min_value,
            max: max_value,
            count: count
        }
    } INTO timeseries_aggregates
    RETURN {metric: metric, points_aggregated: count}
)";
downsample_task.interval = std::chrono::hours(24); // TΓ€glich ausfΓΌhren
scheduler.registerTask(downsample_task);

Speichereinsparung:

  • Vorher: 31.536.000 Datenpunkte/Jahr (1 pro Sekunde)
  • Nachher: 8.760 Datenpunkte/Jahr (1 pro Stunde)
  • Einsparung: 99,97%

Use Case 2: Multi-Tier Retention Policy

Szenario: Gestaffelte Aufbewahrung mit verschiedenen AuflΓΆsungen.

Policy:

  • 0-7 Tage: 1s-AuflΓΆsung (Echtzeit-Analysen)
  • 7 Tage - 3 Monate: 1m-AuflΓΆsung (detaillierte Historie)
  • 3 Monate - 1 Jahr: 1h-AuflΓΆsung (Trendanalysen)
  • > 1 Jahr: 1d-AuflΓΆsung (Langzeit-Archiv)

Speichereinsparung:

Tier 1 (0-7d):     1s   β†’ 100% der Daten
Tier 2 (7d-3m):    1m   β†’ ~1,67% (60:1)
Tier 3 (3m-1y):    1h   β†’ ~0,03% (3600:1)
Tier 4 (>1y):      1d   β†’ ~0,001% (86400:1)

Durchschnittliche Speichereinsparung ΓΌber 5 Jahre: ~95%

Use Case 3: Metrik-spezifische Retention

Szenario: Verschiedene Metriken haben unterschiedliche Anforderungen.

Beispiel:

  • Temperatur: 1s β†’ 1h nach 1 Jahr (niedrige Varianz)
  • Druck: 1s β†’ 15m nach 90 Tagen (hΓΆhere Varianz)
  • Vibration: 1s β†’ 5m nach 30 Tagen (sehr hohe Varianz)
  • Status: Keine Aggregation (diskrete Werte)

Konfiguration:

// Temperatur-Policy
ScheduledTask temp_policy;
temp_policy.parameters = {
    {"metric", "temperature"},
    {"source_resolution", "1s"},
    {"target_resolution", "1h"},
    {"retention_days", 365}
};

// Druck-Policy
ScheduledTask pressure_policy;
pressure_policy.parameters = {
    {"metric", "pressure"},
    {"source_resolution", "1s"},
    {"target_resolution", "15m"},
    {"retention_days", 90}
};

Statistiken und AuswertungsfΓ€higkeit

Erhaltene Metriken

Bei Aggregation werden folgende Statistiken berechnet und gespeichert:

{
  "timestamp": "2024-01-01T12:00:00Z",
  "resolution": "1h",
  "value": 23.5,  // Durchschnittswert
  "statistics": {
    "avg": 23.5,
    "stddev": 1.2,    // Standardabweichung
    "min": 21.0,      // Minimum
    "max": 25.8,      // Maximum
    "count": 3600,    // Anzahl Originaldatenpunkte
    "sum": 84600.0    // Summe (fΓΌr weitere Aggregation)
  }
}

MΓΆgliche Analysen

Mit Aggregaten mΓΆglich: βœ… Trendanalysen (Moving Averages) βœ… Anomalie-Detektion (Outliers via Stddev) βœ… Min/Max-Analysen βœ… Volumenanalysen (Count, Sum) βœ… Histogramme (mit Stddev-SchΓ€tzung) βœ… Korrelationsanalysen

EingeschrÀnkt: ⚠️ Exakte Perzentile (nur SchÀtzung mâglich) ⚠️ Exakte Median-Berechnung ⚠️ Detailanalysen auf Sekundenebene

Genauigkeitsverlust

Aggregation Datenverlust AnalysefΓ€higkeit
1s β†’ 1m Minimal 99% erhalten
1s β†’ 1h Gering 95% erhalten
1s β†’ 1d Moderat 85% erhalten

Empfehlung: FΓΌr langfristige Archive ist 1h-AuflΓΆsung ein guter Kompromiss zwischen Speicher und AnalysefΓ€higkeit.

Konfiguration

Retention Policy Konfiguration

# config.yaml
retention_policies:
  # Globale Einstellungen
  enabled: true
  check_interval_hours: 24
  
  # Policy-Definitionen
  policies:
    - name: "temperature_yearly"
      metric_pattern: "temperature_*"
      source_resolution: "1s"
      target_resolution: "1h"
      retention_days: 365
      statistics: ["avg", "stddev", "min", "max"]
      
    - name: "pressure_quarterly"
      metric_pattern: "pressure_*"
      source_resolution: "1s"
      target_resolution: "15m"
      retention_days: 90
      statistics: ["avg", "stddev", "min", "max"]
      
    - name: "vibration_monthly"
      metric_pattern: "vibration_*"
      source_resolution: "1s"
      target_resolution: "5m"
      retention_days: 30
      statistics: ["avg", "stddev", "min", "max", "p95"]
      
  # Multi-Tier Policy
  multi_tier:
    enabled: true
    tiers:
      - resolution: "1s"
        retention_days: 7
      - resolution: "1m"
        retention_days: 90
      - resolution: "1h"
        retention_days: 365
      - resolution: "1d"
        retention_days: -1  # Forever

Programmatische Konfiguration

// Retention-Policy aus Config laden
struct RetentionConfig {
    std::string name;
    std::string metric_pattern;
    std::string source_resolution;
    std::string target_resolution;
    int retention_days;
    std::vector<std::string> statistics;
};

void configureRetentionPolicies(
    TaskScheduler& scheduler,
    const std::vector<RetentionConfig>& policies
) {
    for (const auto& policy : policies) {
        ScheduledTask task;
        task.name = policy.name;
        task.type = ScheduledTask::TaskType::FUNCTION;
        task.function_name = "apply_retention_policy";
        task.parameters = {
            {"metric", policy.metric_pattern},
            {"source_resolution", policy.source_resolution},
            {"target_resolution", policy.target_resolution},
            {"retention_days", policy.retention_days},
            {"statistics", policy.statistics}
        };
        task.interval = std::chrono::hours(24);
        
        scheduler.registerTask(task);
    }
}

Monitoring

Storage Savings Dashboard

Metriken:

  • retention_raw_records_total - Anzahl Rohdatenpunkte
  • retention_aggregated_records_total - Anzahl Aggregate
  • retention_storage_savings_bytes - Eingesparter Speicher
  • retention_compression_ratio - KompressionsverhΓ€ltnis
  • retention_policy_executions_total - Policy-AusfΓΌhrungen
  • retention_policy_execution_duration_seconds - AusfΓΌhrungszeit

Grafana Dashboard:

{
  "panels": [
    {
      "title": "Storage Savings Over Time",
      "targets": [
        {
          "expr": "retention_storage_savings_bytes / 1024 / 1024 / 1024"
        }
      ]
    },
    {
      "title": "Compression Ratio by Resolution",
      "targets": [
        {
          "expr": "retention_compression_ratio"
        }
      ]
    }
  ]
}

Beispiel-Report

=== Storage Savings Report ===
Period: Last 365 days

Raw Data (1s):
  - Records: 31,536,000
  - Storage: 3,000 MB

Aggregated Data (1h):
  - Records: 8,760
  - Storage: 1.3 MB

Savings:
  - Compression Ratio: 3600:1
  - Storage Saved: 2,998.7 MB (99.96%)
  - Cost Saved: ~$30/month (cloud storage)

Best Practices

1. Backup vor Deletion

// Immer erst aggregieren, dann lΓΆschen
ScheduledTask aggregate_task;
aggregate_task.id = "aggregate_old_data";
// ... Aggregation logic ...

ScheduledTask cleanup_task;
cleanup_task.id = "cleanup_aggregated_data";
cleanup_task.aql_query = R"(
    // Nur lΓΆschen wenn Aggregate existieren
    FOR d IN timeseries
    FILTER d.resolution == '1s'
    LET aggregate_exists = (
        FOR a IN timeseries_aggregates
        FILTER a.timestamp == DATE_TRUNC(d.timestamp, 'hour')
        LIMIT 1
        RETURN 1
    )
    FILTER LENGTH(aggregate_exists) > 0
    REMOVE d IN timeseries
)";

2. Schrittweise Aggregation

// Besser: Stufenweise aggregieren (1s β†’ 1m β†’ 1h)
// Schlechteres: Direkt (1s β†’ 1h)
// Vorteil: Bessere Zwischenstufen fΓΌr Analysen

3. Retention-Windows

// Grace Period: 1 Tag extra behalten fΓΌr Sicherheit
FILTER d.timestamp < DATE_SUB(NOW(), 366, 'days')  // 1 Jahr + 1 Tag

4. Inkrementelle Verarbeitung

// Nur einen Tag pro Run verarbeiten (fΓΌr Performance)
FILTER d.timestamp BETWEEN 
    DATE_SUB(NOW(), 366, 'days') AND 
    DATE_SUB(NOW(), 365, 'days')

Performance-Optimierung

Batch-Grâße optimieren

// Option 1: Zeitbasierte Batches (empfohlen)
FOR d IN timeseries
FILTER d.timestamp BETWEEN '2023-01-01' AND '2023-01-02'
// Verarbeite 1 Tag pro Task

// Option 2: Count-basierte Batches
FOR d IN timeseries
LIMIT 100000
// Verarbeite max 100k Datenpunkte pro Task

Index-Nutzung

-- Erstelle Indices fΓΌr effiziente Queries
CREATE INDEX idx_ts_resolution ON timeseries(resolution, timestamp);
CREATE INDEX idx_ts_metric ON timeseries(metric, timestamp);

Parallel-AusfΓΌhrung

TaskScheduler::Config config;
config.max_concurrent_tasks = 8;  // Mehrere Policies parallel

Kosten-Nutzen-Analyse

Beispiel: 100 IoT-Sensoren

Annahmen:

  • 100 Sensoren
  • 1 Datenpunkt/Sekunde/Sensor
  • 100 Bytes/Datenpunkt
  • 5 Jahre Aufbewahrung

Ohne Retention:

100 Sensoren Γ— 31.536.000 Punkte/Jahr Γ— 5 Jahre Γ— 100 Bytes
= 1.576.800.000.000 Bytes = 1.468 TB
Cloud-Kosten: ~$30.000/Jahr

Mit Multi-Tier Retention:

Tier 1 (0-7d):    100 Γ— 604.800 Γ— 100 Bytes = 5,8 GB
Tier 2 (7d-3m):   100 Γ— 119.520 Γ— 100 Bytes = 1,1 GB
Tier 3 (3m-1y):   100 Γ— 7.884 Γ— 100 Bytes = 75 MB
Tier 4 (>1y):     100 Γ— 1.460 Γ— 100 Bytes Γ— 4 = 58 MB
Total: ~7 GB
Cloud-Kosten: ~$150/Jahr

Einsparung: 99,5% ($29.850/Jahr)

Zusammenfassung

Vorteile βœ…

  • Massive Speichereinsparung: 95-99% fΓΌr langfristige Daten
  • AuswertungsfΓ€higkeit erhalten: Statistische Eigenschaften bleiben
  • Automatisiert: Keine manuelle Intervention nΓΆtig
  • Konfigurierbar: Flexible Policies pro Metrik
  • Skalierbar: Multi-Tier-Ansatz fΓΌr große Datenmengen

Empfehlungen πŸ“‹

  1. Start konservativ: 1s β†’ 1h nach 1 Jahr fΓΌr alle Metriken
  2. Monitoring aufsetzen: Storage Savings Dashboard
  3. Schrittweise optimieren: Metrik-spezifische Policies hinzufΓΌgen
  4. Backups: Immer vor Deletion testen
  5. Grace Period: 1 Tag extra Puffer

NΓ€chste Schritte πŸš€

  1. Retention-Policies in config.yaml definieren
  2. TaskScheduler mit Policies starten
  3. Monitoring-Dashboard einrichten
  4. Nach 1 Monat: Savings analysieren und optimieren
  5. Nach 3 Monaten: Multi-Tier-Policy evaluieren

Referenzen

  • Beispiel-Code: examples/data_retention_downsampling_example.cpp
  • TaskScheduler-Doku: docs/de/scheduler/TASK_SCHEDULER.md
  • Konfiguration: config.yaml (retention_policies Sektion)

ThemisDB Wiki

🏠 Overview

πŸš€ Getting Started

πŸ“– Tutorials

πŸ“— User Guide

βš™οΈ Operations & Security

πŸ“Ÿ Ops Runbooks

πŸ—οΈ Architecture

πŸ“ ADRs

πŸ”§ Contributing

πŸ“‹ Governance

πŸ” Audit

🧩 Plugins

πŸ”Œ Adapters

πŸ’‘ Examples

πŸ“¦ Client SDKs

πŸŽ“ Training

πŸ› οΈ Tools

πŸ€– Developer LLM Wiki

Clone this wiki locally