-
Notifications
You must be signed in to change notification settings - Fork 1
INGESTION_OPTIMIZATION_IDEAS
Erstellt: 25. Dezember 2025
Version: 1.0
Status: Analyse & Empfehlungen
Kategorie: Performance Optimization
Diese Analyse untersucht die Datenübertragung zur ThemisDB-Datenbank und identifiziert konkrete Optimierungsmöglichkeiten für die Ingestion-Pipeline. Die Untersuchung konzentriert sich auf:
- RocksDB Write Path Optimierungen
- HTTP/gRPC Protokoll-Overhead Reduktion
- Batch- und Buffer-Strategien
- Kompression und Serialisierung
- Memory-Mapped I/O und Zero-Copy
- Client-Side Optimierungen
✅ Bestehende Stärken:
- Auto-Buffer für Time Series (TSAutoBuffer) mit 10-50× Throughput-Verbesserung
- VectorAutoBuffer für RAG/Semantic Search implementiert
- Compressed WAL Shipping mit 3-10× Bandbreitenreduktion
- RocksDB WriteBatch für atomare Bulk-Operationen
- Binary Protocol Support für niedrige Latenz
💡 Identifizierte Optimierungspotenziale:
- RocksDB Write Buffer Tuning: +20-40% Schreib-Performance
- HTTP Overhead Reduktion: +30-50% bei binärem Protokoll
- Adaptive Batch Sizing: +15-25% bei variablen Datenmengen
- Payload Compression: 60-90% Bandbreitenreduktion
- Memory-Mapped Bulk Import: +100-300% für große Dateien
Aktuelle Konfiguration:
// src/storage/rocksdb_wrapper.cpp, Zeile 87-89
options_->write_buffer_size = config_.memtable_size_mb * 1024 * 1024; // Default: 256 MB
options_->max_write_buffer_number = config_.max_write_buffer_number; // Default: 3
options_->min_write_buffer_number_to_merge = config_.min_write_buffer_number_to_merge; // Default: 1Problem:
- Zu kleine Memtables verursachen häufige Flushes → höhere Latenz
- Zu große Memtables verzögern Flushes → Memory-Druck
- Nicht optimiert für verschiedene Workload-Profile
Empfohlene Optimierungen:
// Adaptive write buffer configuration basierend auf System-RAM
struct AdaptiveWriteBufferConfig {
size_t system_ram_gb;
size_t write_buffer_size_mb;
int max_write_buffer_number;
size_t db_write_buffer_size_mb;
};
// Empfohlene Konfigurationen
std::vector<AdaptiveWriteBufferConfig> configs = {
// RAM, WB_Size, Max_Num, Total_Size
{8, 128, 3, 384}, // Low-end systems
{16, 256, 4, 1024}, // Standard (current)
{32, 512, 6, 3072}, // Write-heavy workloads
{64, 1024, 6, 6144}, // High-throughput ingestion
{128, 2048, 8, 16384} // Enterprise bulk loading
};Erwarteter Impact:
- Low-end (8GB RAM): Stabil, keine Out-of-Memory
- Standard (16GB): Baseline (aktuell)
- Write-heavy (32GB): +40-60% Write Throughput
- High-throughput (64GB): +100-150% Write Throughput
- Enterprise (128GB): +200-300% Write Throughput
Risiko: Niedrig (gut getestet in RocksDB)
Aktuelle Konfiguration:
// src/storage/rocksdb_wrapper.cpp, Zeile 174-181
options_->allow_concurrent_memtable_write = config_.allow_concurrent_memtable_write; // Default: true
options_->enable_pipelined_write = config_.enable_pipelined_write; // Default: trueProblem:
- Concurrent memtable writes nur bei WRITE_PREPARED policy
- Pipelined writes haben immer noch Lock Contention bei >16 Threads
Empfohlene Optimierung:
// Enhanced parallel write configuration
struct ParallelWriteConfig {
Config::WritePolicy write_policy = Config::WritePolicy::WritePrepared;
bool allow_concurrent_memtable_write = true;
bool enable_pipelined_write = true;
bool two_write_queues = true; // Dual queues (prepare/commit)
uint64_t wp_commit_cache_bits = 23; // 8M commit cache entries
// NEW: Per-thread memtable assignment
bool enable_per_thread_memtable = true; // Reduced lock contention
int num_memtable_shards = 16; // Shard count (power of 2)
};Erwarteter Impact:
- 8 Threads: +10-15% (wenig Contention)
- 16 Threads: +25-40% (moderate Contention)
- 32 Threads: +60-100% (hohe Contention)
- 64+ Threads: +150-250% (extreme Contention)
Implementierungsaufwand: 3-5 Tage
Risiko: Mittel (erfordert RocksDB-Patch oder neueste Version)
Aktuelle Konfiguration:
// src/storage/rocksdb_wrapper.cpp, Zeile 140-142
options_->level0_file_num_compaction_trigger = config_.level0_file_num_compaction_trigger; // Default: 4
options_->level0_slowdown_writes_trigger = config_.level0_slowdown_writes_trigger; // Default: 20
options_->level0_stop_writes_trigger = config_.level0_stop_writes_trigger; // Default: 36Problem:
- Bei hohem Write-Throughput können L0-Files schneller entstehen als Compaction mithalten kann
- Write-Stalls treten auf → Latenz-Spitzen
Empfohlene Optimierung:
// Aggressive Level0 Compaction for High-Throughput Ingestion
struct AggressiveCompactionConfig {
int level0_file_num_compaction_trigger = 2; // Start früher (default: 4)
int level0_slowdown_writes_trigger = 8; // Frühere Warnung (default: 20)
int level0_stop_writes_trigger = 16; // Frühere Blockierung (default: 36)
int max_background_compactions = 8; // Mehr Compaction-Threads
int max_subcompactions = 2; // Parallel sub-compactions
// NEW: Prioritize L0 compactions
bool prioritize_level0_compaction = true;
double level0_compaction_priority_multiplier = 2.0;
};Trade-off:
- ✅ Vermeidet Write-Stalls → niedrigere P99-Latenz
⚠️ Höherer CPU-Verbrauch für Compaction⚠️ Höherer I/O durch häufigere Compactions
Erwarteter Impact:
- P99 Write Latency: -50-70% (eliminiert Stalls)
- CPU-Auslastung: +15-25%
- Sustained Write Throughput: +20-40%
Use Case: Bulk-Import, Streaming-Ingestion mit hohen Raten
Aktuelle Konfiguration:
// src/storage/rocksdb_wrapper.cpp, Zeile 184-188
write_options_->sync = config_.enable_wal; // Default: true
write_options_->disableWAL = config_.disable_wal_for_benchmark; // Default: falseProblem:
- WAL fsync bei jedem Schreibvorgang → hohe Latenz
- Keine Group Commit für kleine Writes
Empfohlene Optimierungen:
// Async WAL Configuration
struct AsyncWALConfig {
bool sync = false; // Async writes (keine fsync)
bool disableWAL = false; // WAL bleibt aktiv
// NEW: Group Commit Settings
size_t wal_bytes_per_sync = 1024 * 1024; // 1 MB (batch fsync)
std::chrono::microseconds max_write_delay{100}; // Max 100µs delay für group commit
size_t max_writes_in_group = 100; // Max 100 writes pro group
};Sicherheits-Trade-off:
- ❌ Datenverlust-Risiko: Bis zu 100µs Daten bei Crash
- ✅ Performance: +200-500% Write Throughput
- ✅ Anwendbar: Bei Read-Replicas, Entwicklungsumgebungen
Anwendungsbeispiel:
// Konfiguration für verschiedene Szenarien
AsyncWALConfig production = {
.sync = true, // Volle Durability
.wal_bytes_per_sync = 0 // Jeder Write synced
};
AsyncWALConfig read_replica = {
.sync = false, // Async WAL
.wal_bytes_per_sync = 1MB, // Batch fsync
.max_write_delay = 100µs // Niedrige Latenz
};
AsyncWALConfig bulk_import = {
.sync = false, // Async WAL
.wal_bytes_per_sync = 64MB, // Große Batches
.max_write_delay = 1000µs // Höhere Latenz akzeptabel
};Empfehlung:
# config/rocksdb.yaml
storage:
db_path: /data/rocksdb # HDD/SATA SSD (bulk storage)
wal_dir: /nvme/rocksdb/wal # NVMe SSD (low latency)
# Multi-path SSTable placement
db_paths:
- path: /nvme/rocksdb/hot
target_size: 100GB # Hot data auf NVMe
- path: /ssd/rocksdb/warm
target_size: 500GB # Warm data auf SATA SSD
- path: /data/rocksdb/cold
target_size: unlimited # Cold data auf HDDErwarteter Impact:
- Write Latency: -30-50% (NVMe für WAL)
- Read Performance: +20-40% (Tiered Storage)
- Kosten: Optimal (nur Hot Data auf teurem Storage)
Aktuelle Situation:
ThemisDB unterstützt mehrere Protokolle:
- HTTP/REST mit JSON (Standard)
-
Binary Protocol mit MessagePack (
buffer_binary_protocol.h) - gRPC (optional, via Plugin)
Overhead-Analyse:
| Protokoll | Payload Size | Parse Time | Latency | Throughput |
|---|---|---|---|---|
| HTTP/JSON | 100% (Baseline) | 100% | 100% | 100% |
| HTTP/MessagePack | 60% | 30% | 70% | 140% |
| Binary/MessagePack | 60% | 20% | 50% | 200% |
| gRPC/Protobuf | 40% | 15% | 40% | 250% |
Beispiel: Time Series Insert
// HTTP/JSON (229 Bytes)
POST /ts/put
{
"metric": "cpu.usage",
"entity": "server-01",
"timestamp": 1703520000,
"value": 75.5,
"tags": {
"datacenter": "us-east-1",
"app": "frontend"
}
}# Binary/MessagePack (87 Bytes, -62%)
# Opcode: 0x70 (TS_PUT_BUFFERED)
# Payload: {
# 'm': 'cpu.usage',
# 'e': 'server-01',
# 't': 1703520000,
# 'v': 75.5,
# 'tags': {'dc': 'us-east-1', 'app': 'fe'}
# }Empfehlung:
Für Bulk-Ingestion und High-Throughput-Szenarien sollte das Binary Protocol bevorzugt werden:
# Python Client - Binary Protocol
from themisdb import BufferedClient
client = BufferedClient('localhost', 9090, protocol='binary')
client.put_ts_buffered('cpu.usage', 'server-01', timestamp, 75.5)Aktuelle Unterstützung:
// include/server/http2_session.h
// include/server/http3_session.h
#ifdef THEMIS_ENABLE_HTTP2
#ifdef THEMIS_ENABLE_HTTP3Problem:
- HTTP/1.1: Head-of-Line Blocking
- Eine langsame Request blockiert alle folgenden Requests
- Kein Request-Pipelining
Empfehlung: HTTP/2 aktivieren
# config/server.yaml
server:
enable_http2: true
http2_max_concurrent_streams: 1000 # Parallel requests
http2_initial_window_size: 65535 # Flow controlErwarteter Impact:
- Parallele Requests: 10-100× mehr concurrent requests
- Latenz: -30-50% (kein Head-of-Line Blocking)
- Throughput: +50-100% bei vielen kleinen Requests
Aktuelle Situation: Keine automatische HTTP-Payload-Kompression implementiert.
Empfohlene Implementierung:
// Content-Encoding Support
enum class CompressionType {
None,
Gzip, // Weit verbreitet, moderate Kompression
Zstd, // Bessere Ratio, schneller
LZ4 // Sehr schnell, moderate Ratio
};
// HTTP Request Handler
http::response<http::string_body> handleRequest(
const http::request<http::string_body>& req) {
auto response = processRequest(req);
// Check Accept-Encoding header
if (req["Accept-Encoding"].find("zstd") != std::string::npos) {
response.set(http::field::content_encoding, "zstd");
response.body() = compress_zstd(response.body());
} else if (req["Accept-Encoding"].find("gzip") != std::string::npos) {
response.set(http::field::content_encoding, "gzip");
response.body() = compress_gzip(response.body());
}
return response;
}Kompressionsraten:
| Datentyp | Gzip | Zstd | LZ4 |
|---|---|---|---|
| JSON Metadata | 70-80% | 75-85% | 60-70% |
| Time Series (Doubles) | 40-60% | 50-70% | 30-50% |
| Text/Logs | 80-90% | 85-92% | 70-80% |
| Embeddings (Float32) | 10-20% | 15-25% | 5-15% |
Empfehlung:
- Standard: Zstd Level 3 (bestes Trade-off)
- Low-Latency: LZ4 (minimal CPU)
- Maximum Compression: Zstd Level 19 (für Archivierung)
Problem: Feste Batch-Größen sind nicht optimal für variable Datenmengen:
- Kleine Batches: Overhead durch häufige Commits
- Große Batches: Höhere Latenz, mehr Memory
Empfohlene Implementierung:
// Adaptive Batch Controller
class AdaptiveBatchController {
public:
struct Config {
size_t min_batch_size = 10;
size_t max_batch_size = 10000;
size_t target_batch_latency_ms = 50; // Target: 50ms pro Batch
size_t memory_limit_mb = 100;
// Learning parameters
double alpha = 0.1; // Learning rate
std::chrono::milliseconds sample_window{10000}; // 10s window
};
// Dynamisch angepasste Batch-Größe
size_t getCurrentBatchSize() const {
return current_batch_size_;
}
// Feedback nach Batch-Commit
void recordBatchMetrics(size_t batch_size,
std::chrono::milliseconds latency,
size_t memory_used) {
// Update moving averages
avg_latency_ = (1 - alpha_) * avg_latency_ + alpha_ * latency.count();
// Adjust batch size
if (avg_latency_ > config_.target_batch_latency_ms) {
// Latenz zu hoch -> kleinere Batches
current_batch_size_ = std::max(
config_.min_batch_size,
current_batch_size_ * 0.9
);
} else if (avg_latency_ < config_.target_batch_latency_ms * 0.5) {
// Latenz niedrig -> größere Batches
current_batch_size_ = std::min(
config_.max_batch_size,
current_batch_size_ * 1.1
);
}
}
private:
Config config_;
size_t current_batch_size_;
double avg_latency_;
double alpha_;
};Erwarteter Impact:
- Variable Workloads: +15-25% Durchsatz
- Latenz-Stabilität: -20-30% P99 Latency
- Memory-Effizienz: +10-20%
Konzept:
Client Buffer (100ms)
↓
Server Buffer (1s)
↓
RocksDB WriteBatch (sync)
Implementierung:
// Three-tier buffering architecture
class ThreeTierBuffer {
public:
// Tier 1: Client-side buffer (lowest latency)
class ClientBuffer {
std::deque<Record> buffer_;
std::chrono::milliseconds flush_interval_{100}; // 100ms
void add(const Record& record) {
buffer_.push_back(record);
if (shouldFlush()) flush();
}
};
// Tier 2: Server-side buffer (medium latency)
class ServerBuffer {
std::deque<Record> buffer_;
std::chrono::milliseconds flush_interval_{1000}; // 1s
size_t max_size_ = 10000;
void add(const Record& record) {
buffer_.push_back(record);
if (shouldFlush()) flush();
}
};
// Tier 3: RocksDB WriteBatch (atomicity)
void flushToRocksDB(const std::vector<Record>& records) {
auto batch = storage_->createWriteBatch();
for (const auto& r : records) {
batch->put(r.key, r.value);
}
batch->commit();
}
};Vorteile:
- Client: Niedrige Latenz (in-memory)
- Server: Aggregation & Kompression
- RocksDB: Atomare Commits mit WriteBatch
Problem: Alle Daten werden gleich behandelt, unabhängig von Wichtigkeit oder Latenz-Anforderungen.
Empfohlene Implementierung:
// Priority-based buffer queues
enum class Priority {
Critical = 0, // Real-time alerts, high-value transactions
High = 1, // User-facing operations
Normal = 2, // Standard ingestion
Low = 3, // Batch imports, backfills
Background = 4 // Analytics, archival
};
class PriorityBufferQueue {
public:
void add(const Record& record, Priority priority) {
queues_[static_cast<int>(priority)].push_back(record);
}
std::vector<Record> getNextBatch(size_t max_size) {
// Priority-based selection: Critical > High > Normal > Low > Background
for (auto& queue : queues_) {
if (!queue.empty()) {
size_t batch_size = std::min(max_size, queue.size());
std::vector<Record> batch(queue.begin(), queue.begin() + batch_size);
queue.erase(queue.begin(), queue.begin() + batch_size);
return batch;
}
}
return {};
}
private:
std::array<std::deque<Record>, 5> queues_; // 5 priority levels
};Use Cases:
- Critical: Financial transactions, alerts
- High: User API requests
- Normal: Standard ingestion
- Low: Batch imports
- Background: Analytics processing
Aktuelle Situation: Embeddings werden als Float32 gespeichert (4 Bytes pro Dimension).
Problem:
- 768D Embedding = 3072 Bytes
- 1 Million Embeddings = 3 GB
- Keine Kompression → hoher Memory/Disk-Verbrauch
Empfohlene Optimierung: Product Quantization
// Product Quantization für Embeddings
class ProductQuantizer {
public:
struct Config {
int dimension = 768;
int num_subvectors = 96; // 768/8 = 96 subvectors
int codebook_bits = 8; // 256 codes per subvector
bool use_opq = true; // Optimized Product Quantization
};
// Trainiere Codebooks
void train(const std::vector<std::vector<float>>& embeddings) {
// 1. Optional: Rotate (OPQ)
if (config_.use_opq) {
rotation_matrix_ = computeOptimalRotation(embeddings);
embeddings = rotate(embeddings, rotation_matrix_);
}
// 2. Split in subvectors
auto subvectors = splitIntoSubvectors(embeddings);
// 3. Train k-means für jeden Subvektor (256 clusters)
for (int i = 0; i < config_.num_subvectors; i++) {
codebooks_[i] = trainKMeans(subvectors[i], 256);
}
}
// Komprimiere Embedding
std::vector<uint8_t> encode(const std::vector<float>& embedding) {
std::vector<uint8_t> codes(config_.num_subvectors);
for (int i = 0; i < config_.num_subvectors; i++) {
codes[i] = findNearestCode(embedding, i);
}
return codes; // 768 floats → 96 bytes (32x Kompression!)
}
// Dekomprimiere Embedding (approximativ)
std::vector<float> decode(const std::vector<uint8_t>& codes) {
std::vector<float> embedding(config_.dimension);
for (int i = 0; i < config_.num_subvectors; i++) {
auto centroid = codebooks_[i][codes[i]];
std::copy(centroid.begin(), centroid.end(),
embedding.begin() + i * (config_.dimension / config_.num_subvectors));
}
return embedding;
}
};Kompressionsraten:
| Methode | Größe | Recall@10 | Suche |
|---|---|---|---|
| Float32 (Original) | 100% | 100% | 100% |
| Int8 Quantization | 25% | 95-98% | 90% |
| Int16 Quantization | 50% | 98-99% | 95% |
| Product Quantization (PQ) | 3-6% | 90-95% | 80% |
| OPQ (Optimized PQ) | 3-6% | 93-97% | 85% |
Erwarteter Impact:
- Speicher: -90-97% (32× Kompression mit PQ)
- Disk I/O: -90-97%
- Recall: -3-5% (akzeptabel für viele Use Cases)
- Suchgeschwindigkeit: +20-40% (weniger Daten zu laden)
Use Cases:
- RAG/Semantic Search mit Millionen Dokumenten
- Image/Video Embeddings
- Knowledge Graph Embeddings
Bereits implementiert in TSAutoBuffer! ✅
// include/timeseries/ts_auto_buffer.h
// Gorilla compression: 10-20x compression für Double-Werte
class TSAutoBuffer {
// XOR-based delta encoding
// Timestamp delta-of-delta encoding
// Leading/trailing zero suppression
};Performance:
- Kompression: 10-20× für typische Time Series
- CPU-Overhead: Minimal (<5%)
- Bereits produktiv im Einsatz ✅
Problem: Große JSON-Payloads werden uncompressed übertragen.
Empfohlene Client-Side Implementierung:
# Python Client mit Payload-Kompression
import zstandard as zstd
import requests
class CompressedClient:
def __init__(self, url):
self.url = url
self.compressor = zstd.ZstdCompressor(level=3)
def post(self, endpoint, data):
# Serialisiere zu JSON
json_data = json.dumps(data).encode('utf-8')
# Komprimiere mit Zstd
compressed = self.compressor.compress(json_data)
# Sende mit Content-Encoding header
response = requests.post(
f"{self.url}{endpoint}",
data=compressed,
headers={
'Content-Type': 'application/json',
'Content-Encoding': 'zstd'
}
)
return responseServer-Side Handler:
// Automatische Dekompression im HTTP Handler
http::response<http::string_body> handleRequest(
const http::request<http::string_body>& req) {
std::string body = req.body();
// Check Content-Encoding
auto encoding = req["Content-Encoding"];
if (encoding == "zstd") {
body = decompress_zstd(body);
} else if (encoding == "gzip") {
body = decompress_gzip(body);
}
// Parse JSON
auto payload = json::parse(body);
// ... process request
}Erwarteter Impact:
- JSON Metadata: 70-85% Reduktion
- Network Traffic: -70-85%
- Client CPU: +5-10%
- Server CPU: +5-10%
Trade-off: Akzeptabel für WAN, weniger sinnvoll für lokale Netzwerke.
Problem: Große Dateien werden vollständig in Memory geladen vor dem Import.
Empfohlene Implementierung:
// Memory-mapped bulk import
class MemoryMappedImporter {
public:
Status importFromFile(const std::string& filepath) {
// 1. Memory-map file (kein Memory-Overhead)
int fd = open(filepath.c_str(), O_RDONLY);
struct stat st;
fstat(fd, &st);
void* mapped = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
madvise(mapped, st.st_size, MADV_SEQUENTIAL); // Hint: sequential read
// 2. Parse in chunks (keine Kopie)
size_t offset = 0;
while (offset < st.st_size) {
// Parse chunk direkt aus Memory-Map
auto records = parseChunk(static_cast<char*>(mapped) + offset, chunk_size);
// Batch-Import
importBatch(records);
offset += chunk_size;
}
// 3. Cleanup
munmap(mapped, st.st_size);
close(fd);
return Status::OK();
}
};Vorteile:
- Kein Memory-Overhead: OS managed page cache
- Lazy Loading: Nur benötigte Pages werden geladen
- Zero-Copy: Direkte Verarbeitung aus Memory-Map
Erwarteter Impact:
- Memory-Verbrauch: -80-95% (keine Duplikation)
- Import-Geschwindigkeit: +100-300% (bei großen Dateien)
- Startup-Zeit: Instant (kein Pre-Loading)
Problem: Daten werden mehrfach kopiert: Network Buffer → User Space → RocksDB
Empfohlene Optimierung:
// Zero-copy network receive
class ZeroCopyReceiver {
public:
Status receiveAndStore(int socket_fd) {
// 1. Prepare RocksDB WriteBatch
auto batch = storage_->createWriteBatch();
// 2. Direct socket → WriteBatch (keine Zwischenkopie)
char buffer[4096];
while (true) {
// Recv direkt in Batch-Buffer
ssize_t bytes = recv(socket_fd, buffer, sizeof(buffer), 0);
if (bytes <= 0) break;
// Parse und direkt in Batch schreiben
auto records = parseRecords(buffer, bytes);
for (const auto& r : records) {
batch->put(r.key, std::string_view(buffer + r.value_offset, r.value_size));
}
}
// 3. Single commit
return batch->commit();
}
};Erwarteter Impact:
- Memory Copies: 3 → 1 (67% Reduktion)
- CPU-Verbrauch: -20-30%
- Latenz: -15-25%
Aktuelle Konfiguration:
// src/storage/rocksdb_wrapper.cpp
options_->use_direct_reads = false;
options_->use_direct_io_for_flush_and_compaction = false;Empfohlene Aktivierung für Bulk Import:
// Direct I/O configuration for bulk ingestion
struct DirectIOConfig {
bool use_direct_reads = false; // Standard: false
bool use_direct_io_for_flush_and_compaction = true; // Bulk: true
// NEW: Conditional direct I/O
bool enable_direct_io_during_bulk_import = true;
};Vorteile:
- OS Cache Bypass: Kein Thrashing bei großen Imports
- Predictable Performance: Keine OS-Cache-Effekte
- Memory-Effizienz: Mehr Memory für RocksDB Block Cache
Trade-off:
- ✅ Bulk Import: +30-50% Performance
⚠️ Normal Operations: -10-20% Performance (kein OS Cache)
Empfehlung: Nur für Bulk-Import aktivieren, danach deaktivieren.
Problem: Jeder Request öffnet eine neue TCP-Verbindung → hoher Overhead.
Empfohlene Implementierung:
# Python Client mit Connection Pool
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class PooledClient:
def __init__(self, base_url, pool_size=100):
self.base_url = base_url
self.session = requests.Session()
# Configure connection pool
adapter = HTTPAdapter(
pool_connections=pool_size,
pool_maxsize=pool_size,
max_retries=Retry(
total=3,
backoff_factor=0.1,
status_forcelist=[500, 502, 503, 504]
)
)
self.session.mount('http://', adapter)
self.session.mount('https://', adapter)
def post(self, endpoint, data):
return self.session.post(f"{self.base_url}{endpoint}", json=data)Erwarteter Impact:
- Connection Overhead: -80-90%
- Latenz: -20-40% (keine TCP Handshake)
- Throughput: +50-100%
HTTP/2 Client:
# HTTP/2 Client mit Multiplexing
import httpx
class HTTP2Client:
def __init__(self, base_url):
self.base_url = base_url
self.client = httpx.AsyncClient(http2=True)
async def post_many(self, requests):
# Send alle Requests parallel über HTTP/2
tasks = [
self.client.post(f"{self.base_url}{req['endpoint']}", json=req['data'])
for req in requests
]
# Wait für alle Responses
responses = await asyncio.gather(*tasks)
return responsesErwarteter Impact:
- Parallele Requests: 10-100× mehr
- Latenz: -40-60% (kein Head-of-Line Blocking)
- Throughput: +100-300%
Problem: Viele kleine Requests statt wenige große Batches.
Empfohlene Implementierung:
# Auto-Batching Client
import time
from collections import deque
from threading import Thread, Lock
class AutoBatchingClient:
def __init__(self, base_url, batch_size=1000, flush_interval=1.0):
self.base_url = base_url
self.batch_size = batch_size
self.flush_interval = flush_interval
self.buffer = deque()
self.lock = Lock()
# Background flush thread
self.flush_thread = Thread(target=self._flush_loop, daemon=True)
self.flush_thread.start()
def add(self, endpoint, data):
with self.lock:
self.buffer.append({'endpoint': endpoint, 'data': data})
if len(self.buffer) >= self.batch_size:
self._flush()
def _flush(self):
if not self.buffer:
return
with self.lock:
batch = list(self.buffer)
self.buffer.clear()
# Send batch request
response = requests.post(
f"{self.base_url}/batch",
json={'operations': batch}
)
def _flush_loop(self):
while True:
time.sleep(self.flush_interval)
self._flush()Erwarteter Impact:
- Request Count: -99% (1000:1 Batching)
- Overhead: -95-99%
- Throughput: +500-1000%
| Optimierung | Impact | Aufwand | ROI |
|---|---|---|---|
| Adaptive Write Buffer Sizing | +40-60% | 2 Tage | ⭐⭐⭐⭐⭐ |
| HTTP/2 Aktivierung | +50-100% | 1 Tag | ⭐⭐⭐⭐⭐ |
| Client Connection Pooling | +50-100% | 2 Tage | ⭐⭐⭐⭐⭐ |
| Payload Compression (Zstd) | -70-85% Traffic | 3 Tage | ⭐⭐⭐⭐ |
| Level0 Compaction Tuning | -50-70% P99 Latency | 2 Tage | ⭐⭐⭐⭐ |
Gesamtimpact: +100-200% Throughput, -60-80% Latency
| Optimierung | Impact | Aufwand | ROI |
|---|---|---|---|
| Product Quantization für Embeddings | -90-97% Speicher | 2 Wochen | ⭐⭐⭐⭐ |
| Memory-Mapped File Import | +100-300% | 1 Woche | ⭐⭐⭐⭐ |
| Per-Thread Memtables | +150-250% (@64 Threads) | 1 Monat | ⭐⭐⭐ |
| Direct I/O für Bulk Import | +30-50% | 1 Woche | ⭐⭐⭐ |
| Zero-Copy Network Transfers | -20-30% CPU | 2 Wochen | ⭐⭐⭐ |
Gesamtimpact: +200-500% für spezifische Workloads
| Optimierung | Impact | Aufwand | ROI |
|---|---|---|---|
| Async WAL mit Group Commit | +200-500% | 3 Wochen | ⭐⭐⭐⭐⭐ |
| Adaptive Batch Sizing | +15-25% | 1 Monat | ⭐⭐⭐ |
| Priority-based Queues | Bessere QoS | 2 Wochen | ⭐⭐⭐ |
| Multi-Level Buffering | +20-40% | 1 Monat | ⭐⭐⭐ |
# config/rocksdb.yaml
storage:
memtable_size_mb: 256
max_write_buffer_number: 4
db_write_buffer_size_mb: 1024
max_background_jobs: 4
level0_file_num_compaction_trigger: 4
level0_slowdown_writes_trigger: 20
level0_stop_writes_trigger: 36
enable_http2: true
compression: "zstd"storage:
memtable_size_mb: 1024
max_write_buffer_number: 6
db_write_buffer_size_mb: 6144
max_background_jobs: 16
max_background_compactions: 8
max_subcompactions: 2
level0_file_num_compaction_trigger: 2
level0_slowdown_writes_trigger: 8
level0_stop_writes_trigger: 16
enable_http2: true
compression: "zstd"
# Separate WAL auf NVMe
wal_dir: /nvme/rocksdb/walstorage:
memtable_size_mb: 2048
max_write_buffer_number: 8
db_write_buffer_size_mb: 16384
max_background_jobs: 16
# Aggressive compaction
level0_file_num_compaction_trigger: 2
max_background_compactions: 12
# Direct I/O
use_direct_io_for_flush_and_compaction: true
# Async WAL (Trade-off: Datenverlust-Risiko)
sync: false
wal_bytes_per_sync: 67108864 # 64 MB- ✅ Adaptive Write Buffer Sizing implementieren
- ✅ HTTP/2 aktivieren und testen
- ✅ Client Connection Pooling in SDKs
- ✅ Payload Compression (Zstd) aktivieren
- ✅ Level0 Compaction tunen
Erwarteter Gewinn: +100-200% Throughput
- ✅ Product Quantization für Embeddings
- ✅ Memory-Mapped File Import
- ✅ Direct I/O für Bulk Import
⚠️ Per-Thread Memtables (RocksDB-Patch)
Erwarteter Gewinn: +200-500% für spezifische Workloads
⚠️ Async WAL mit Group Commit (Trade-off Durability)- ✅ Adaptive Batch Sizing
- ✅ Priority-based Queues
- ✅ Multi-Level Buffering
- RocksDB Tuning Guide
- RocksDB Performance Benchmarks
- HTTP/2 Performance Best Practices
- Product Quantization Paper
- Gorilla Time Series Compression
- Zstandard Compression
- Memory-Mapped I/O Performance
Autor: ThemisDB Performance Team
Review: Engineering Lead
Status: Ready for Implementation
- Architecture-ACCESS-MODEL-IMPLEMENTATION-SUMMARY
- Architecture-ADR-003-pg-dump-sql-parser
- Architecture-BASEENTITY-PRINCIPLE
- Architecture-CACHE-STORAGE-INTEGRATION
- Architecture-CMAKE-ARCHITECTURE
- Architecture-CMAKE-FLAGS-REFERENCE
- Architecture-CMAKE-MODULAR-ARCHITECTURE
- Architecture-CONCERNS-ARCHITECTURE-DIAGRAM
- Architecture-CONCERNS-IMPLEMENTATION-SUMMARY
- Architecture-CONTENT-MODEL
- Architecture-COPILOT-THEMISDB-GRAPH-RAG-BACKEND-ARCHITECTURE
- Architecture-CRYPTO-AND-KEYS
- Architecture-FEATURE-FLAGS-REFERENCE
- Architecture-GPU-ARCHITECTURE-REVIEW-TEMPLATE
- Architecture-HTTP-SHUTDOWN-HARDENING
- Architecture-MIGRATION-GUIDE-CONCERNS
- Architecture-MIGRATION-GUIDE-v13-v14
- Architecture-MODULARIZATION-GUIDE
- Architecture-MODULAR-ARCHITECTURE-ROADMAP
- Architecture-MODULE-ARCHITECTURE-INDEX
- Architecture-P1D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D08-MAMBA-GOVERNANCE-CONTRACT
- Architecture-P1-P2-IMPLEMENTATION-COMPLETION-INDEX
- Architecture-PHASE0-COMPLETION-ASSESSMENT
- Architecture-PHASE3-QUERYENGINE-DI-ARCHITECTURE
- Architecture-PHASE4-INDEX-MANAGER-DI
- Architecture-POSTGRESQL-WIRE-PROTOCOL
- Architecture-QUERYENGINE-IMPLEMENTATION-GUIDE
- Architecture-QUERY-SCHEDULING
- Architecture-RAFT-CONSENSUS-DESIGN
- Architecture-README
- Architecture-README-SSM-HYBRID-IMPLEMENTATION
- Architecture-REFACTORING-SUMMARY
- Architecture-RESOURCE-POOLING
- Architecture-SOURCE-DIRECTORY-GUIDE
- Architecture-THEMIS-CORE-GUIDE
- Architecture-UNIFIED-ACCESS-MODEL
- Architecture-WAL-GRPC-MTLS-CONFIGURATION
- Architecture-WIRE-PROTOCOL-RETRY
- Architecture-boltzmann-observability-draft
- Architecture-experimental-logarithmic-vector-storage
- Architecture-llm-wiki-mvp-adr
- Architecture-rewrite-engine-architecture
- Architecture-rope-api-architecture
- Architecture-ssm-gguf-mamba-status
- Architecture-ssm-hybrid-analysis
- Architecture-ssm-hybrid-rollout-plan
- Architecture-ssm-plugin-interface-design-review
- Architecture-transaction-coordinators
- Architecture-wiki-secondary-index
- Architecture-wire-protocol
- Governance-DISABLED-STUB-POLICY
- Governance-DOCS-PR-POLICY
- Governance-GA-PROMOTION-SIGN-OFF
- Governance-GITHUB-MILESTONES-SETUP
- Governance-MATURITY-CLAIM-VERIFICATION-CHECKLIST
- Governance-MATURITY-EVIDENCE-REGISTRY
- Governance-MERGE-GATE-BOT-CONFIG
- Governance-MERGE-GATE-STATUS-LIVE
- Governance-PHASE3-ENFORCEMENT-RUNBOOK
- Governance-PHASE-1-CLOSURE-REPORT
- Governance-PHASE-CLOSURE-POLICY
- Governance-PHASE-DEPENDENCY-GRAPH
- Governance-PLUGIN-SUBMODULE-ROLLBACK
- Governance-PRODUCTION-READY-2026-DELIVERY-PLAN
- Governance-PR-VERSION-TARGETING
- Governance-PR-VERSION-TARGETING-BACKFILL
- Governance-QUERY-MODULE-STATUS
- Governance-README
- Governance-RELEASE-PROMOTION-GATE-POLICY
- Governance-RELEASE-VALIDATION-CHECKLIST
- Governance-SECURITY-MODULE-5671-EVIDENCE-SUMMARY
- Governance-SHARDING-P6-RESIDUAL-RISK-ACCEPTANCE
- Governance-SOURCECODE-COMPLIANCE-GOVERNANCE
- Governance-UPDATES-DEVELOPMENT-STATUS-SIGN-OFF
- Governance-WAVE-C-IMPLEMENTATION-COMPLETE
- Module-acceleration-Roadmap
- Module-access-model-Roadmap
- Module-ai-Roadmap
- Module-analytics-Roadmap
- Module-api-Roadmap
- Module-aql-Roadmap
- Module-auth-Roadmap
- Module-base-Roadmap
- Module-cache-Roadmap
- Module-cdc-Roadmap
- Module-chaos-Roadmap
- Module-chimera-Roadmap
- Module-config-Roadmap
- Module-content-Roadmap
- Module-core-Roadmap
- Module-distributed-knowledge-Roadmap
- Module-distributed-tensor-Roadmap
- Module-document-Roadmap
- Module-ethics-ai-Roadmap
- Module-evaluation-Roadmap
- Module-execution-Roadmap
- Module-exporters-Roadmap
- Module-failover-Roadmap
- Module-geo-Roadmap
- Module-governance-Roadmap
- Module-gpu-Roadmap
- Module-graph-Roadmap
- Module-image-analysis-Roadmap
- Module-importers-Roadmap
- Module-index-Roadmap
- Module-ingestion-Roadmap
- Module-llama-cpp-Roadmap
- Module-llm-Roadmap
- Module-llm-streaming-Roadmap
- Module-llm-wiki-Roadmap
- Module-maintenance-Roadmap
- Module-metadata-Roadmap
- Module-network-Roadmap
- Module-observability-Roadmap
- Module-onnx-clip-Roadmap
- Module-performance-Roadmap
- Module-plugins-Roadmap
- Module-process-Roadmap
- Module-projects-Roadmap
- Module-prompt-engineering-Roadmap
- Module-query-Roadmap
- Module-rag-Roadmap
- Module-replication-Roadmap
- Module-retrieval-Roadmap
- Module-rpc-grpc-Roadmap
- Module-scheduler-Roadmap
- Module-scraper-Roadmap
- Module-search-Roadmap
- Module-security-Roadmap
- Module-server-Roadmap
- Module-sharding-Roadmap
- Module-stable-diffusion-Roadmap
- Module-storage-Roadmap
- Module-temporal-Roadmap
- Module-tensor-Roadmap
- Module-themis-Roadmap
- Module-timeseries-Roadmap
- Module-toolbox-Roadmap
- Module-training-Roadmap
- Module-transaction-Roadmap
- Module-updates-Roadmap
- Module-user-storage-encrypted-Roadmap
- Module-utils-Roadmap
- Module-vector-search-Roadmap
- Module-voice-Roadmap
- Module-whisper-Roadmap