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:#ffcccc

CPU 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 saturasiload_avg / cpu_count > 1 berarti 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.

← Sebelumnya: Firewall   Berikutnya: OOM Killer →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact