Saturation #
Sistem yang kewalahan jarang langsung mati. Ia biasanya memberikan peringatan terlebih dahulu — latency yang perlahan naik, queue yang terus bertumbuh, error rate yang mulai merangkak — sebelum akhirnya tidak bisa melayani request apapun. Peringatan-peringatan ini adalah sinyal saturasi: kondisi di mana satu atau beberapa resource dalam sistem sudah mendekati atau melampaui kapasitas maksimalnya.
Saturation adalah salah satu konsep paling penting dalam reliability engineering. Ia berbeda dari utilization (seberapa banyak resource digunakan) dan errors (apakah terjadi kegagalan). Saturation mengukur seberapa banyak pekerjaan yang sedang menunggu untuk dilayani — antrian yang terbentuk karena supply tidak bisa mengimbangi demand. Sebuah sistem yang CPU-nya berjalan di 70% tidak jenuh. Sistem yang CPU-nya 70% tapi ada 50 proses yang mengantri di run queue sudah mulai jenuh.
Memahami saturation memberi engineer kemampuan untuk memprediksi kapan sistem akan bermasalah sebelum masalah terjadi, bukan hanya bereaksi setelah user mengeluh.
USE Method: Kerangka Kerja untuk Memahami Saturasi #
Brendan Gregg memperkenalkan USE Method — Utilization, Saturation, Errors — sebagai kerangka kerja sistematis untuk mendiagnosis performa sistem. Ketiganya harus selalu diukur bersama karena memberikan gambaran yang berbeda:
USE Method:
Utilization:
→ Seberapa sibuk resource (persentase waktu digunakan)
→ CPU 85% = utilisasi tinggi
→ Tapi utilisasi tinggi belum tentu berarti saturasi
Saturation:
→ Seberapa banyak pekerjaan yang MENUNGGU untuk dilayani
→ Load average > jumlah CPU = saturasi
→ Queue panjang = saturasi
→ Ini yang sebenarnya menyebabkan latency naik
Errors:
→ Apakah ada operasi yang gagal
→ Disk write error, network packet loss, allocation failure
→ Seringkali muncul setelah saturasi sudah parah
Hubungan ketiganya:
Utilisasi rendah + tidak ada saturasi = sistem sehat
Utilisasi tinggi + tidak ada saturasi = sibuk tapi masih sehat
Utilisasi tinggi + saturasi tinggi = sistem kewalahan
Utilisasi rendah + saturasi tinggi = ada bottleneck (resource tidak efisien)
graph LR
A[Resource] --> B{Utilization}
B --> |Rendah| C[Sehat]
B --> |Tinggi| D{Saturation?}
D --> |Tidak ada antrian| E[Sibuk tapi sehat]
D --> |Ada antrian| F[Sistem jenuh]
F --> G[Latency naik]
F --> H[Throughput turun]
F --> I[Errors mulai muncul]
style F fill:#ffcccc
style G fill:#ffcccc
style H fill:#ffcccc
style I fill:#ffccccCPU Saturation #
CPU saturation terjadi ketika ada lebih banyak proses/thread yang butuh CPU daripada CPU yang tersedia. Proses yang tidak mendapat CPU masuk ke run queue dan menunggu.
# Mengukur CPU saturation di Linux
# Load average — cara tercepat untuk deteksi CPU saturation
uptime
# Output: load average: 4.20, 3.85, 3.10
# Angka pertama: 1 menit, kedua: 5 menit, ketiga: 15 menit
# Interpretasi load average:
# Load average = jumlah CPU → utilisasi 100%, batas saturasi
# Load average > jumlah CPU → SATURASI — ada proses yang menunggu
# Cek jumlah CPU:
nproc # atau: cat /proc/cpuinfo | grep "^processor" | wc -l
# Jika load average 4.20 dan CPU = 4:
# 4.20 / 4 = 1.05 → sedikit di atas kapasitas → mulai saturasi
# Cara lebih detail dengan vmstat:
vmstat 1 10 # sampling setiap 1 detik, 10 kali
# Kolom yang penting:
# r = run queue (proses yang menunggu CPU) ← ini yang mengukur saturasi
# b = proses yang blocked (menunggu I/O)
# us = user CPU (%)
# sy = system CPU (%)
# id = idle CPU (%)
# wa = wait I/O (%)
# Contoh output yang mengkhawatirkan:
# procs memory swap io system cpu
# r b swpd free buff cache si so bi bo in cs us sy id wa
# 8 2 1024 512 128 4096 0 0 2048 1024 500 800 85 10 0 5
# r=8 dengan 4 CPU → 4 proses menunggu → saturasi!
# top / htop untuk real-time monitoring
top
# Perhatikan:
# - %Cpu(s): us+sy = total usage
# - Load average (kanan atas)
# - VIRT/RES/SHR per proses
# sar untuk historical data
sar -q 1 5 # load average dan run queue history
# atau: sar -u 1 5 # CPU utilization history
# Deteksi CPU saturation secara programatik
import psutil
import time
def check_cpu_saturation(threshold_multiplier: float = 1.5) -> dict:
"""
Cek apakah CPU dalam kondisi saturasi.
threshold_multiplier: jika load_avg > cpu_count * multiplier → saturasi
"""
cpu_count = psutil.cpu_count()
load_avg_1min = psutil.getloadavg()[0]
utilization = psutil.cpu_percent(interval=1)
# Saturation ratio: > 1.0 berarti ada proses yang menunggu
saturation_ratio = load_avg_1min / cpu_count
is_saturated = saturation_ratio > threshold_multiplier
return {
'cpu_count': cpu_count,
'load_avg_1min': load_avg_1min,
'utilization_pct': utilization,
'saturation_ratio': round(saturation_ratio, 2),
'is_saturated': is_saturated,
'severity': (
'critical' if saturation_ratio > 2.0 else
'warning' if saturation_ratio > 1.5 else
'ok'
)
}
# Contoh output:
# {'cpu_count': 4, 'load_avg_1min': 7.2, 'utilization_pct': 95.0,
# 'saturation_ratio': 1.8, 'is_saturated': True, 'severity': 'warning'}
Penyebab umum CPU saturation dan cara mitigasinya:
Penyebab:
→ Traffic yang terlalu tinggi untuk kapasitas server
→ Query database yang mahal (full table scan, complex join)
→ Proses yang tidak efisien (algoritma O(n²), regex yang lambat)
→ GC (Garbage Collection) yang sering dan lama di JVM/Go
→ Thread yang terlalu banyak dan sering context switching
Mitigasi jangka pendek:
→ Scale horizontal (tambah server, auto-scaling)
→ Kill atau nice proses yang tidak penting
→ Rate limiting untuk mengurangi beban
Mitigasi jangka panjang:
→ Profiling dan optimasi kode yang CPU-intensive
→ Caching untuk mengurangi komputasi berulang
→ Pisahkan workload berat ke background worker
→ Review dan optimasi query database
Memory Saturation #
Memory saturation terjadi ketika sistem kehabisan RAM fisik dan terpaksa menggunakan swap — area disk yang jauh lebih lambat. Ini adalah bottleneck yang sangat berdampak karena akses disk bisa ribuan kali lebih lambat dari akses RAM.
# Mengukur memory saturation
# free: gambaran umum penggunaan memory
free -h
# Output:
# total used free shared buff/cache available
# Mem: 16Gi 12Gi 500Mi 512Mi 3Gi 3.5Gi
# Swap: 8Gi 4Gi 4Gi
# Yang perlu diperhatikan:
# available: memori yang bisa langsung dipakai aplikasi (lebih akurat dari 'free')
# Swap used: jika swap digunakan → tanda saturasi memory
# vmstat untuk melihat swap activity
vmstat 1 10
# Kolom si (swap in) dan so (swap out):
# si > 0 atau so > 0 → sistem sedang swapping → saturasi!
# Kolom b (blocked processes):
# Proses yang blocked karena menunggu page dari swap
# Pemantauan yang lebih detail
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree|SwapCached"
# Cek proses dengan penggunaan memory tertinggi
ps aux --sort=-%mem | head -20
# OOM (Out of Memory) killer events:
dmesg | grep -i "oom\|killed process"
# Jika ada ini → sistem pernah kehabisan memory dan OS membunuh proses
# Monitor memory saturation
import psutil
def check_memory_saturation() -> dict:
"""Cek kondisi memory dan deteksi saturasi."""
mem = psutil.virtual_memory()
swap = psutil.swap_memory()
# Persentase memory yang tersedia
available_pct = mem.available / mem.total * 100
# Swap digunakan = sinyal saturasi
swap_used_pct = swap.percent if swap.total > 0 else 0
is_saturated = (
available_pct < 10 or # kurang dari 10% tersedia
swap_used_pct > 50 # swap lebih dari 50% terpakai
)
return {
'total_gb': round(mem.total / (1024**3), 1),
'available_gb': round(mem.available / (1024**3), 1),
'used_pct': mem.percent,
'available_pct': round(available_pct, 1),
'swap_used_pct': swap_used_pct,
'swap_total_gb': round(swap.total / (1024**3), 1),
'is_saturated': is_saturated,
'severity': (
'critical' if available_pct < 5 or swap_used_pct > 80 else
'warning' if available_pct < 15 or swap_used_pct > 30 else
'ok'
)
}
Memory saturation — tanda-tanda dan mitigasi:
Tanda-tanda saturasi memory:
→ Swap digunakan secara aktif (vmstat: si/so > 0)
→ Response time naik tajam (akses swap = ribuan kali lebih lambat dari RAM)
→ OOM killer membunuh proses secara otomatis
→ Aplikasi crash dengan error "cannot allocate memory"
Mengapa swap bukan solusi:
→ Disk SSD: ~100.000 IOPS, latensi ~0.1ms
→ RAM: miliaran operasi/detik, latensi ~100 nanosecond
→ Swap adalah solusi darurat, bukan kapasitas tambahan
Mitigasi:
→ Tambah RAM (vertical scaling)
→ Optimize memory usage di aplikasi
→ Pastikan tidak ada memory leak
→ Konfigurasi JVM heap yang tepat
→ Pisahkan workload ke server berbeda
→ Pertimbangkan in-memory caching dengan eviction policy
Disk I/O Saturation #
I/O saturation terjadi ketika disk tidak bisa mengimbangi kecepatan write dan read yang diminta sistem.
# Mengukur disk I/O saturation
# iostat — tool utama untuk I/O analysis
iostat -x 1 10 # extended stats, setiap detik, 10 kali
# Kolom penting di iostat -x:
# %util = seberapa sibuk disk (persentase waktu disk aktif)
# await = rata-rata waktu tunggu per request (ms) ← termasuk queue time
# svctm = rata-rata waktu service per request (ms) ← hanya service time
# avgqu = rata-rata panjang queue ← ini yang mengukur saturasi!
# r/s = read requests per second
# w/s = write requests per second
# Interpretasi:
# %util mendekati 100% → disk sangat sibuk
# avgqu > 1 → ada antrian → saturasi!
# await >> svctm → request menunggu lama di queue → saturasi
# Contoh output yang menunjukkan saturasi:
# Device r/s w/s rkB/s wkB/s await svctm %util avgqu
# sda 50 200 400 3200 45.2 2.1 98.5 8.3
# avgqu=8.3 → 8 request menunggu di queue → saturasi parah!
# iotop: siapa yang paling banyak menggunakan disk
iotop -o # -o: hanya tampilkan proses yang sedang I/O
# Melihat latency per operasi (lebih detail)
biolatency 1 # dari bcc-tools, membutuhkan kernel eBPF support
# Monitoring I/O saturation dengan psutil
import psutil
import time
def check_disk_saturation(device: str = None) -> dict:
"""Cek apakah disk dalam kondisi saturasi."""
# Sampling dua kali untuk menghitung rate
disk_io_1 = psutil.disk_io_counters(perdisk=True)
time.sleep(1)
disk_io_2 = psutil.disk_io_counters(perdisk=True)
results = {}
for disk_name, io2 in disk_io_2.items():
if device and disk_name != device:
continue
io1 = disk_io_1.get(disk_name)
if not io1:
continue
read_bytes = io2.read_bytes - io1.read_bytes
write_bytes = io2.write_bytes - io1.write_bytes
read_time = io2.read_time - io1.read_time
write_time = io2.write_time - io1.write_time
# Busy time dalam persen (approximation)
busy_time = (read_time + write_time) / 10 # ms to pct of 1 second
results[disk_name] = {
'read_mb_s': round(read_bytes / 1024 / 1024, 1),
'write_mb_s': round(write_bytes / 1024 / 1024, 1),
'busy_pct': min(100, round(busy_time, 1)),
'is_saturated': busy_time > 80,
}
return results
Penyebab dan mitigasi I/O saturation:
Penyebab umum:
→ Full table scan tanpa index di database besar
→ Log yang ditulis tanpa buffering
→ Backup yang berjalan selama jam sibuk
→ N+1 query yang menghasilkan ribuan disk access
→ Database yang cache-nya terlalu kecil
Mitigasi:
→ Index yang tepat untuk mengurangi disk read
→ Buffer pool/cache database yang lebih besar
→ SSD atau NVMe untuk menggantikan HDD
→ Pisahkan log ke disk terpisah
→ Jadwalkan backup di luar jam sibuk
→ RAID atau storage terdistribusi
→ Database read replica untuk mengurangi beban read di primary
Network Saturation #
Network saturation terjadi ketika bandwidth yang tersedia tidak cukup untuk semua traffic yang harus melewatinya.
# Mengukur network saturation
# sar -n DEV: historical network stats
sar -n DEV 1 10
# Kolom penting:
# rxkB/s = kilobytes per second received
# txkB/s = kilobytes per second transmitted
# rxdrop/s = dropped receive packets (tanda saturasi!)
# txdrop/s = dropped transmit packets (tanda saturasi!)
# iftop: real-time bandwidth per connection
iftop -n -i eth0
# nload: simple bandwidth monitoring
nload eth0
# ss: socket statistics (koneksi dan buffer)
ss -s # summary
ss -ti # TCP info dengan detail
# Cek receive buffer saturation:
ss -ti | grep "rcv_space\|rcvbuf"
# Buffer penuh = network saturation di level socket
# netstat: packet stats termasuk drops
netstat -s | grep -i "drop\|overflow"
# receive buffer errors > 0 → socket buffer overflow → saturasi!
# ip -s link: interface statistics termasuk drop
ip -s link show eth0
# RX: bytes packets errors dropped
# Jika dropped > 0 → network saturation atau buffer overflow
Network saturation — apa yang terjadi:
Skenario 1: Bandwidth penuh
→ rxkB/s atau txkB/s mendekati batas bandwidth interface (1Gbps, 10Gbps)
→ Packet mulai di-drop di kernel atau di network device
→ Latency naik karena packet retransmission
Skenario 2: Socket buffer overflow
→ Application tidak bisa consume data secepat data datang
→ Receive buffer penuh → packet di-drop
→ Terjadi meski bandwidth belum penuh
Skenario 3: Connection table exhaustion
→ Terlalu banyak concurrent connection
→ SYN flood atau legitimate high traffic
Mitigasi:
→ Upgrade bandwidth (10Gbps → 100Gbps)
→ Kompresi data untuk mengurangi bandwidth usage
→ CDN untuk static assets (kurangi traffic ke origin)
→ Connection pooling untuk mengurangi overhead
→ Perbesar socket buffer:
# sysctl -w net.core.rmem_max=134217728
# sysctl -w net.core.wmem_max=134217728
Application-Level Saturation: Thread Pool dan Connection Pool #
Saturation tidak hanya terjadi di level hardware. Thread pool dan connection pool yang habis adalah bentuk saturasi di level aplikasi.
# Monitoring thread pool saturation (ThreadPoolExecutor Python)
from concurrent.futures import ThreadPoolExecutor
import threading
class MonitoredThreadPool:
"""Thread pool dengan monitoring untuk deteksi saturasi."""
def __init__(self, max_workers: int):
self.max_workers = max_workers
self.executor = ThreadPoolExecutor(max_workers=max_workers)
self._pending_tasks = 0
self._lock = threading.Lock()
def submit(self, fn, *args, **kwargs):
with self._lock:
self._pending_tasks += 1
# Ukur saturation sebelum submit
active = len([t for t in threading._active.values()])
pending = self._pending_tasks
if pending > self.max_workers * 2:
# Queue sudah > 2x capacity → saturasi!
logger.warning(
"Thread pool saturated",
extra={
'pending_tasks': pending,
'max_workers': self.max_workers,
'saturation_ratio': round(pending / self.max_workers, 2)
}
)
future = self.executor.submit(fn, *args, **kwargs)
future.add_done_callback(lambda f: self._on_done())
return future
def _on_done(self):
with self._lock:
self._pending_tasks = max(0, self._pending_tasks - 1)
@property
def saturation_ratio(self) -> float:
return self._pending_tasks / self.max_workers
# Database connection pool saturation (SQLAlchemy)
from sqlalchemy import event, pool
from sqlalchemy import create_engine
def setup_pool_monitoring(engine):
"""Setup monitoring untuk database connection pool."""
@event.listens_for(engine, 'checkout')
def on_checkout(dbapi_connection, connection_record, connection_proxy):
# Connection diambil dari pool
pool_status = engine.pool.status()
# Format: "Pool size: 10 Connections in pool: 3 Current Overflow: 0"
logger.debug("Connection checked out", extra={'pool_status': pool_status})
@event.listens_for(engine, 'checkin')
def on_checkin(dbapi_connection, connection_record):
pass # connection dikembalikan
@event.listens_for(engine.pool, 'connect')
def on_connect(dbapi_connection, connection_record):
current_size = engine.pool.checkedout()
max_size = engine.pool.size() + engine.pool.overflow()
if current_size >= max_size * 0.9:
logger.warning(
"Database connection pool near saturation",
extra={
'checked_out': current_size,
'max_connections': max_size,
'saturation_pct': round(current_size / max_size * 100)
}
)
engine = create_engine(
DATABASE_URL,
pool_size=20,
max_overflow=10,
pool_timeout=30,
pool_pre_ping=True,
)
setup_pool_monitoring(engine)
Metrik Saturation yang Wajib Dimonitor #
# Kumpulkan semua saturation metrics dalam satu tempat
import psutil
import time
from dataclasses import dataclass
@dataclass
class SaturationReport:
timestamp: float
cpu_load_ratio: float # load_avg / cpu_count
memory_available_pct: float # tersedia dalam %
swap_used_pct: float # swap terpakai dalam %
disk_busy_pct: dict # per disk device
has_warnings: bool
critical_resources: list[str]
def generate_saturation_report() -> SaturationReport:
"""Generate laporan saturation untuk semua resource."""
warnings = []
critical = []
# CPU
cpu_count = psutil.cpu_count()
load_avg = psutil.getloadavg()[0]
load_ratio = load_avg / cpu_count
if load_ratio > 2.0:
critical.append(f"CPU: load ratio {load_ratio:.1f}x (critical)")
elif load_ratio > 1.5:
warnings.append(f"CPU: load ratio {load_ratio:.1f}x (warning)")
# Memory
mem = psutil.virtual_memory()
swap = psutil.swap_memory()
avail_pct = mem.available / mem.total * 100
swap_pct = swap.percent
if avail_pct < 5:
critical.append(f"Memory: only {avail_pct:.1f}% available (critical)")
elif avail_pct < 15:
warnings.append(f"Memory: only {avail_pct:.1f}% available (warning)")
if swap_pct > 50:
warnings.append(f"Swap: {swap_pct:.0f}% used")
# Disk (simplified)
disk_stats = {}
try:
io = psutil.disk_io_counters(perdisk=True)
for disk, stats in io.items():
disk_stats[disk] = {'available': True}
except Exception:
pass
return SaturationReport(
timestamp=time.time(),
cpu_load_ratio=round(load_ratio, 2),
memory_available_pct=round(avail_pct, 1),
swap_used_pct=round(swap_pct, 1),
disk_busy_pct=disk_stats,
has_warnings=bool(warnings or critical),
critical_resources=critical
)
Alert Threshold yang Berguna #
# Contoh konfigurasi alert (Prometheus AlertManager format konseptual)
# CPU saturation
alert: CPUSaturation
expr: node_load1 / count(node_cpu_seconds_total{mode="idle"}) without (cpu, mode) > 1.5
for: 5m
labels:
severity: warning
annotations:
summary: "CPU saturation detected"
description: "Load average is {{ $value }}x the number of CPUs"
# Memory saturation
alert: MemorySaturation
expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.10
for: 5m
labels:
severity: warning
annotations:
summary: "Low memory available"
description: "Only {{ $value | humanizePercentage }} memory available"
# Swap usage
alert: SwapUsageHigh
expr: node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes < 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "High swap usage"
description: "More than 50% of swap is in use"
# Disk saturation
alert: DiskIOSaturation
expr: rate(node_disk_io_time_seconds_total[5m]) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Disk I/O saturation"
description: "Disk {{ $labels.device }} is {{ $value | humanizePercentage }} busy"
# Network drops
alert: NetworkDrops
expr: rate(node_network_receive_drop_total[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "Network packets being dropped"
Mencegah Saturasi: Capacity Planning #
Deteksi saturasi bersifat reaktif. Capacity planning adalah pendekatan proaktif untuk memastikan sistem tidak mencapai titik saturasi secara mengejutkan.
Framework capacity planning sederhana:
1. Ukur baseline saat ini:
→ CPU utilization rata-rata dan peak
→ Memory usage rata-rata dan peak
→ Disk I/O rata-rata dan peak
→ Network throughput rata-rata dan peak
→ Request rate dan latency
2. Tentukan safety margin:
→ Jangan operasikan sistem di atas 70% utilisasi untuk resource kritikal
→ Sisakan 30% headroom untuk traffic spike dan pertumbuhan
→ Tanpa headroom, saturasi terjadi saat ada spike kecil
3. Proyeksikan pertumbuhan:
→ Berapa persen pertumbuhan traffic per bulan?
→ Kapan utilisasi akan mencapai 70% berdasarkan pertumbuhan itu?
→ Rencanakan scaling sebelum titik itu tercapai
4. Load testing:
→ Uji sistem pada kondisi peak yang lebih tinggi dari ekspektasi
→ Temukan bottleneck sebelum production menemukannya
→ Identifikasi resource mana yang pertama kali jenuh
5. Auto-scaling:
→ Horizontal: tambah instance saat CPU/memory mencapai threshold
→ Vertical: upgrade instance size untuk resource yang tidak bisa di-scale horizontal
→ Pastikan auto-scaling cukup cepat merespons sebelum sistem saturasi
Anti-Pattern yang Harus Dihindari #
✗ Anti-pattern 1: monitoring hanya utilization, bukan saturation
CPU 80% terlihat oke. Tapi load average 16 di server 4-core
menunjukkan 12 proses menunggu → sistem sudah sangat jenuh.
✓ Monitor load average dan queue depth, bukan hanya persentase.
✗ Anti-pattern 2: bereaksi setelah user mengeluh
User melaporkan aplikasi lambat → investigasi → ditemukan saturasi.
Saturasi sudah berlangsung mungkin sejak jam yang lalu.
✓ Pasang alert proaktif untuk saturation metrics.
✗ Anti-pattern 3: scaling saat sudah kritis
Auto-scaling dipicu saat CPU 95% → perlu waktu untuk instance baru ready
→ selama itu sistem dalam kondisi saturasi parah.
✓ Trigger scaling lebih awal (threshold 60-70%), bukan saat sudah kritis.
✗ Anti-pattern 4: tidak ada load test
Kapasitas sistem di production tidak diketahui sampai production collapse.
✓ Load test reguler untuk menemukan batas kapasitas sebelum production.
✗ Anti-pattern 5: mengabaikan swap
"Swap masih ada, berarti masih aman."
Swap yang aktif digunakan adalah tanda system sudah dalam kondisi darurat.
✓ Alert saat swap mulai digunakan, bukan saat swap sudah penuh.
Checklist Saturation Monitoring #
MEASUREMENT:
□ Load average dimonitor relatif terhadap jumlah CPU (bukan angka absolut)
□ Memory: available_pct dimonitor (bukan hanya free)
□ Swap usage dimonitor dan di-alert jika mulai digunakan
□ Disk: avgqu (average queue depth) dimonitor via iostat
□ Network: packet drops dimonitor
□ Application: thread pool queue depth dimonitor
□ Database: connection pool saturation dimonitor
ALERTING:
□ CPU: alert jika load ratio > 1.5x selama lebih dari 5 menit
□ Memory: alert jika available < 15%
□ Swap: alert jika swap digunakan > 30%
□ Disk: alert jika disk busy > 80% selama lebih dari 5 menit
□ Network: alert jika ada packet drops
□ DB pool: alert jika connection pool > 80% terpakai
CAPACITY PLANNING:
□ Baseline metrics terdokumentasi
□ Growth rate dihitung dan diproyeksikan
□ Safety margin 30% diterapkan untuk semua resource kritikal
□ Load testing dilakukan secara berkala
□ Auto-scaling dikonfigurasi dengan threshold yang tepat (tidak terlalu late)
INCIDENT RESPONSE:
□ Runbook tersedia untuk setiap jenis saturasi
□ Eskalasi otomatis jika saturasi berlanjut setelah N menit
□ Post-mortem dilakukan setelah setiap insiden saturasi
Ringkasan #
- Saturation mengukur antrian, bukan utilisasi — utilisasi 80% tidak berarti sistem jenuh. Yang berarti jenuh adalah adanya antrian: load average melebihi jumlah CPU, request menunggu di thread pool, query menunggu di connection pool.
- USE Method memberikan gambaran lengkap — Utilization, Saturation, dan Errors harus selalu diukur bersama. Saturation adalah yang paling langsung berkorelasi dengan degradasi performa yang dirasakan user.
- Load average di atas jumlah CPU adalah sinyal CPU saturasi —
load_avg / cpu_count > 1berarti ada proses yang menunggu. Di atas 1.5 perlu perhatian, di atas 2.0 perlu tindakan segera.- Swap yang aktif digunakan adalah kondisi darurat — sistem sudah kehabisan RAM dan menggunakan disk yang ribuan kali lebih lambat. Alert harus berbunyi saat swap mulai digunakan, bukan saat sudah penuh.
- Disk saturation diukur dari avgqu, bukan %util — disk 100% util tapi tidak ada antrian masih bisa melayani dengan baik. Queue depth yang tinggi menunjukkan request menunggu terlalu lama.
- Application-level saturation sama pentingnya — thread pool yang exhausted dan connection pool yang habis adalah bentuk saturasi yang tidak terlihat di metrik OS, tapi sangat terasa di user.
- Scale sebelum saturasi, bukan saat saturasi — auto-scaling yang dipicu saat sistem sudah saturasi terlalu lambat. Trigger di 60-70% utilisasi memberikan waktu untuk instance baru siap sebelum sistem kewalahan.
- Load testing menemukan batas sebelum production — jangan tunggu production untuk menemukan kapasitas maksimum sistem. Load test reguler memberikan data yang diperlukan untuk capacity planning.
- Safety margin 30% adalah minimum — operasi sistem di 70% utilisasi secara konsisten memberikan ruang untuk spike traffic tanpa langsung mencapai saturasi.
- Packet drops adalah sinyal network saturation yang paling jelas — berbeda dari CPU dan memory yang bisa dimonitor via utilization, network saturation paling akurat terdeteksi dari drops.