Penerapan algoritma kompresi dan pengindeksan teks mutakhir sering kali menjanjikan efisiensi throughput dan rasio kompresi tinggi. Namun, perubahan mendasar pada implementasi algoritma inti dapat memicu perilaku patologis pada beban kerja riil di production. Artikel ini mengulas studi kasus insiden saturasi CPU dan lonjakan latensi pada service log indexing engine berskala besar, yang dipicu oleh migrasi dari Suffix BWT ke Cyclic Shift BWT.

Konteks Arsitektur dan Pemicu Insiden

Sistem log indexing engine menerima aliran log terstruktur dan un-structured dari ribuan microservice via Apache Kafka. Komponen ingest worker mengelompokkan payload log ke dalam blok berukuran 4 MB, lalu membangun indeks teks berbasis Burrows-Wheeler Transform (BWT) dan Run-Length Encoding (RLE) agar pencarian substring dapat dieksekusi langsung pada representasi terkompresi tanpa dekompresi penuh.

Sebelum rilis, tim melakukan optimasi untuk meniadakan karakter sentinel akhir ($) guna menghemat overhead memori dan penyusunan string. Tim mengganti algoritma konstruksi dari Suffix BWT (yang memanfaatkan algoritma SA-IS standar pada string dengan sentinel) menjadi Cyclic Shift BWT (mengurutkan pergeseran siklik langsung).

Setelah rilis canary sebesar 10% trafik diarahkan ke pod baru, instans worker langsung mengalami lonjakan utilisasi CPU hingga 100%, memicu throttling ekstrim dan kegagalan pemrosesan antrean log.

Observability: Deteksi via Golden Signals

Regresi algoritma ini terdeteksi dalam kurun waktu 3 menit setelah canary deployment dimulai melalui pemantauan four golden signals pada Prometheus dan Grafana:

  • Latency (p99 Ingestion Latency): Latensi pemrosesan batch 4 MB melonjak dari baseline 25 ms menjadi lebih dari 3.800 ms.
  • Saturation (CPU Throttling): Metrik container_cpu_cfs_throttled_periods_total pada container worker melonjak dari 0,2% menjadi 88%, mengindikasikan container kehabisan kuota CPU CFS (Completely Fair Scheduler) Kubernetes.
  • Traffic / Ingestion Lag: Metrik Kafka kafka_consumergroup_lag pada topik log ingestion naik eksponensial dengan laju +120.000 pesan per detik karena worker gagal melakukan commit offset tepat waktu.
  • Errors: Liveness probe Kubernetes mulai gagal merespons dalam ambang batas 5 detik, menyebabkan status CrashLoopBackOff berantai pada pod canary.
# Prometheus Alert Rule: Deteksi dini CPU Throttling kritis
- alert: HighCPUThrottlingCanary
  expr: sum(rate(container_cpu_cfs_throttled_periods_total{namespace="logging", pod=~"log-indexer-canary-.*"}[1m])) 
        / sum(rate(container_cpu_cfs_periods_total{namespace="logging", pod=~"log-indexer-canary-.*"}[1m])) > 0.30
  for: 2m
  labels:
    severity: critical
  annotations:
    summary: "Canary log indexer mengalami CPU throttling parah (> 30%)"

Root Cause Analysis: Suffix BWT vs Cyclic Shift BWT

Akar masalah terletak pada karakteristik payload log produksi yang mengandung redundansi periodik tinggi, seperti stack trace Java berulang, ASCII art separator, dan baris log identik yang dihasilkan loop aplikasi pengguna.

Secara teoretis:

  • Suffix BWT: Membutuhkan karakter sentinel unik (terminator $) yang leksikografis lebih kecil dari seluruh karakter lainnya. Karena keberadaan sentinel ini, tidak ada sufiks yang menjadi prefiks dari sufiks lainnya. Algoritma linear seperti SA-IS (Suffix Array by Induced Sorting) menyelesaikan pemilahan ini dalam waktu deterministik O(N) tanpa bergantung pada redundansi internal teks.
  • Cyclic Shift BWT: Mengurutkan seluruh N rotasi siklik dari string T. Jika string memiliki periode repetitif p di mana p << N (contoh: T = (pattern)^k), banyak rotasi siklik yang memiliki prefiks identik sepanjang O(N). Implementasi canary menggunakan konstruksi berbasis doubling string T + T dan perbandingan leksikografis konvensional tanpa dekomposisi Lyndon (Duval's algorithm) yang memadai. Akibatnya, kompleksitas waktu memburuk secara patologis menjadi O(N^2) pada data log yang repetitif.
// Contoh simulasi bottleneck: Komparator rotasi siklik naif
// Membandingkan rotasi siklik pada string periodik tinggi memicu O(N^2)
func NaiveCyclicCompare(text string, i, j int) int {
    n := len(text)
    for k := 0; k < n; k++ {
        ci := text[(i+k)%n]
        cj := text[(j+k)%n]
        if ci != cj {
            if ci < cj {
                return -1
            }
            return 1
        }
    }
    return 0 // Worst case: teks periodik identik memeriksa seluruh n karakter
}
Komputasi Cyclic Shift BWT tanpa struktur faktorisasi Lyndon atau sentinelisasi eksplisit sangat rentan terhadap serangan kompleksitas waktu kuadratik saat menghadapi string highly-repetitive.

Prosedur Mitigasi dan Fast Canary Rollback

Karena arsitektur deployment menggunakan Argo Rollouts, tim on-call mengeksekusi mitigasi cepat begitu alert latency p99 dan consumer lag terpicu.

  1. Abort Canary Rollout: Menghentikan injeksi trafik ke replica set canary dan mengalihkan 100% beban kerja kembali ke versi stabil (Suffix BWT).
  2. Scale Down Pod Canary: Menghapus pod canary yang sedang mengalami CPU hang untuk melepaskan partisi Kafka kembali ke pod sehat.
# Perintah mitigasi melalui Argo Rollouts CLI
kubectl argo rollouts abort log-indexer-deployment -n logging
kubectl argo rollouts set-image log-indexer-deployment indexer=log-indexer:v2.14.3-stable -n logging

Dalam 90 detik pasca rollback, metrik CPU throttling turun ke level 0%, throughput konsumsi Kafka kembali ke 450 MB/s, dan lag antrean selesai di-drain dalam waktu 8 menit tanpa kehilangan data (zero data loss).

Format Postmortem Terstruktur

Berikut adalah ringkasan format postmortem insiden yang diterapkan tim rekayasa:

  • Ringkasan Insiden: Degradasi performa ingest log akibat CPU saturation pada pod canary indexer v2.15.0-rc1 selama 14 menit.
  • Dampak: Log delivery delay hingga 9 menit bagi 15% pengguna cluster. Tidak ada data yang drop karena retensi Kafka 72 jam.
  • Akar Masalah (RCA): Algoritma Cyclic Shift BWT degradasi ke O(N^2) saat memproses string dengan redundansi periodik tinggi, menghabiskan alokasi kuota CFS scheduler container.
  • 5-Whys:
    1. Kenapa consumer lag melonjak? Pod worker terjebak komputasi CPU 100%.
    2. Kenapa CPU 100%? Waktu komputasi BWT satu blok 4 MB naik dari 25 ms ke 4.000 ms.
    3. Kenapa waktu komputasi BWT naik drastis? Algoritma pergeseran siklik mengalami worst-case depth comparison.
    4. Kenapa pengujian pra-rilis lolos? Benchmark CI hanya menggunakan dataset log acak sintetis (non-repetitif).
    5. Kenapa tidak menggunakan Suffix BWT? Tim mencoba optimasi mikro untuk memangkas alokasi 1 byte terminator per chunk.

Langkah Pencegahan Konkret

Untuk mencegah kejadian serupa, tiga tindakan preventif teknis diimplementasikan pada pipeline dan runtime:

1. Guardrail Performance Benchmark pada Pipeline CI/CD

Benchmark performa wajib menyertakan skenario kasus terburuk (pathological repetitive payload). Pengujian dihentikan secara otomatis bila rasio instruksi per byte melampaui batas ambang baseline.

// Benchmark guardrail: menguji string dengan periodisitas ekstrem
func BenchmarkBWTPathological(b *testing.B) {
    repetitiveData := bytes.Repeat([]byte("ERROR [2026-03-30 10:00:00] Connection reset by peer
"), 5000)
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = ComputeBWT(repetitiveData) // Gagal otomatis jika melampaui SLA waktu deterministik
    }
}

2. Circuit Breaker pada Payload Pre-processing

Tambahkan deteksi periodisitas cepat berbasis Shannon Entropy atau Run-Length Check sederhana sebelum memproses blok data ke modul kompresi. Jika payload terdeteksi memiliki periodisitas ekstrem, chunk dialihkan ke algoritma fallback (Dictionary-based LZ4/Zstandard) tanpa memicu komputasi BWT penuh.

3. Isolasi Resource Worker

Terapkan pembatasan CPU quota dengan cpu.cfs_quota_us yang proporsional dan hindari pemakaian thread tunggal yang dapat memblokir thread event loop Tokio/Go Runtime. Pisahkan thread pool komputasi intensif (BWT computation) dari worker antrean jaringan menggunakan pool terisolasi berkapasitas thread terbatas.