Laporan bulanan menyebut angka yang menenangkan: 30 dari 30 run mart_penjualan sukses. Namun dua run baru selesai pukul 09:40 dan 07:55 — setelah tim finance mulai bekerja — dan satu run memuat baris kurang dari ekspektasi. Konsumen data tidak mengalami “sukses”; konsumen mengalami data yang terlambat atau kurang lengkap. Service level objective (SLO) menerjemahkan janji “data siap dan benar” menjadi angka yang bisa diukur, ditargetkan, dan dipakai mengambil keputusan rilis. Chapter penutup section ini menyusun SLO khusus data dan menghitung pencapaiannya nyata dari run ledger.

SLA, SLO, dan SLI #

Chapter High Availability telah membahas matematika SLA infrastruktur — 99,9% berarti 43,8 menit downtime per bulan. Untuk pipeline data, kerangkanya bertingkat: SLI adalah ukuran, SLO adalah target internal atas SLI, dan SLA adalah kontrak ke pihak eksternal yang melanggar konsekuensinya. Tabel berikut memisahkannya:

IstilahDefinisiContoh untuk platform data
SLIindikator terukur dari perilaku layanan28 dari 30 run selesai sebelum 06:00
SLOtarget internal atas SLI dalam periode waktu95% run selesai sebelum 06:00 per bulan
SLAkontrak eksternal dengan konsekuensikoreksi tagihan bila penyedia warehouse di bawah 99,9%

Kerangka lengkapnya diuraikan di Google SRE Book: Service Level Objectives . Aturan praktisnya: jangan pernah menjanjikan SLA kepada konsumen sebelum SLO internal tercapai tiga bulan berturut-turut.

SLI untuk Pipeline Data #

Tiga SLI yang paling sering dipakai untuk tabel mart: freshness (berapa lama sejak run terakhir yang baik — untuk batch harian, “selesai sebelum jam kerja”), completeness (jumlah baris dibanding ekspektasi — pola yang sama dengan penghitungan baris pada Chapter Investigasi Kegagalan Data), dan correctness sebagai proxy (proporsi tes data yang lulus). Ketiganya dihitung otomatis dari data yang sudah kita miliki: run ledger.

Laporan SLO Bulanan dari Run Ledger #

Skrip berikut membaca run ledger Desember (satu baris per run terjadwal: tanggal, jam selesai, baris, ekspektasi, hasil tes), mengevaluasi tiap run terhadap tiga SLI, lalu menghitung pencapaian bulanan dan pemakaian error budget:

python
"""Laporan SLO bulanan mart_penjualan TokoKita: hitung SLI freshness,
completeness, dan correctness dari run ledger Desember; bandingkan dengan
target SLO; hitung pemakaian error budget; turunkan ambang alert burn-rate."""

SLO = {"freshness": 0.95, "completeness": 0.99, "correctness": 0.95}
AMBANG_FRESH = "06:00"     # mart wajib siap sebelum jam kerja
AMBANG_COMPLETE = 0.995    # baris >= 99.5% ekspektasi
AMBANG_TEST = 0.98         # proporsi tes data yang lulus
HARI = 30


def ledger():
    """Run ledger Desember 2026 (dipendekkan; satu baris per run terjadwal)."""
    pola = ["04:12", "04:35", "05:02", "04:48", "04:20", "05:10", "04:55"]
    for hari in range(1, HARI + 1):
        jam = {3: "09:40", 17: "07:55"}.get(hari, pola[hari % 7])
        ekspektasi = 850 + hari * 8              # promo akhir bulan menaikkan
        baris = int(ekspektasi * 0.989) if hari == 17 else ekspektasi
        tes = (47, 48) if hari == 17 else (48, 48)
        yield (f"2026-12-{hari:02d}", jam, baris, ekspektasi, tes[0], tes[1])


def jam_ke(teks):
    return int(teks[:2]) + int(teks[3:]) / 60


DEFINISI = {
    "freshness": f"selesai <= {AMBANG_FRESH}",
    "completeness": f"baris >= {AMBANG_COMPLETE:.1%} ekspektasi",
    "correctness": f"tes lulus >= {AMBANG_TEST:.0%}",
}
buruk = {sli: [] for sli in SLO}
for tgl, jam, baris, eks, lulus, total in ledger():
    if jam_ke(jam) > jam_ke(AMBANG_FRESH):
        buruk["freshness"].append(tgl)
    if baris < AMBANG_COMPLETE * eks:
        buruk["completeness"].append(tgl)
    if lulus / total < AMBANG_TEST:
        buruk["correctness"].append(tgl)

print(f"Run ledger Desember: {HARI} run terjadwal")
print("\n| SLI | Run good bila | SLO | Hasil | Budget | Terpakai | Status |")
print("|---|---|---|---|---|---|---|")
gagal = []
for sli, target in SLO.items():
    baik = HARI - len(buruk[sli])
    hasil = baik / HARI
    budget = (1 - target) * HARI
    status = "LULUS" if hasil >= target else "GAGAL"
    if status == "GAGAL":
        gagal.append(sli)
    print(f"| {sli} | {DEFINISI[sli]} | {target:.0%} | {baik}/{HARI} "
          f"({hasil:.1%}) | {budget:.1f} run | {len(buruk[sli])} run |"
          f" {status} |")

print("\nRun yang melanggar tiap SLI:")
for sli in SLO:
    if buruk[sli]:
        print(f"  {sli}: {', '.join(buruk[sli])}")

if gagal:
    print(f"\nKeputusan: error budget {' dan '.join(gagal)} habis ->"
          f" rilis fitur Januari ditunda sampai budget pulih")

# Ambang alert burn-rate untuk freshness: budget 5% dari 720 jam = 36 jam
JAM_BULAN, BUDGET_JAM = 720, 36
print(f"\nAmbang burn-rate freshness (budget {BUDGET_JAM} jam / {JAM_BULAN} jam):")
print("| Burn rate | Jendela | Konsumsi budget | Ambang stale di jendela | Aksi |")
print("|---|---|---|---|---|")
for burn, jendela, aksi in ((14.4, 1, "page"), (6, 6, "page"),
                            (3, 24, "ticket"), (1, 72, "ticket")):
    konsumsi = burn * jendela / JAM_BULAN
    ambang = konsumsi * BUDGET_JAM
    print(f"| {burn}x | {jendela} jam | {konsumsi:.0%} | {ambang:.2f} jam |"
          f" {aksi} |")

Output yang Dihasilkan:

text
Run ledger Desember: 30 run terjadwal

| SLI | Run good bila | SLO | Hasil | Budget | Terpakai | Status |
|---|---|---|---|---|---|---|
| freshness | selesai <= 06:00 | 95% | 28/30 (93.3%) | 1.5 run | 2 run | GAGAL |
| completeness | baris >= 99.5% ekspektasi | 99% | 29/30 (96.7%) | 0.3 run | 1 run | GAGAL |
| correctness | tes lulus >= 98% | 95% | 29/30 (96.7%) | 1.5 run | 1 run | LULUS |

Run yang melanggar tiap SLI:
  freshness: 2026-12-03, 2026-12-17
  completeness: 2026-12-17
  correctness: 2026-12-17

Keputusan: error budget freshness dan completeness habis -> rilis fitur Januari ditunda sampai budget pulih

Ambang burn-rate freshness (budget 36 jam / 720 jam):
| Burn rate | Jendela | Konsumsi budget | Ambang stale di jendela | Aksi |
|---|---|---|---|---|
| 14.4x | 1 jam | 2% | 0.72 jam | page |
| 6x | 6 jam | 5% | 1.80 jam | page |
| 3x | 24 jam | 10% | 3.60 jam | ticket |
| 1x | 72 jam | 10% | 3.60 jam | ticket |

Mari kita telaah baris-baris kode di atas:

  1. Baris 8-11 (target dan ambang): SLO 95% untuk freshness dan correctness, tetapi 99% untuk completeness — target per SLI diatur terpisah sesuai toleransi konsumen, bukan satu angka seragam.
  2. Baris 15-24 (ledger): pola jam normal bersiklus mingguan; dua hari bermasalah dimasukkan lewat override sehingga data terlihat seperti run ledger sungguhan, bukan angka bulat.
  3. Baris 36-44 (evaluasi per run): setiap run dinilai tiga SLI sekaligus; tanggal yang melanggar dikumpulkan agar laporan bisa menunjuk hari spesifik.
  4. Baris 52-63 (budget dan status): error budget = (1 - SLO) × jumlah run; status GAGAL memicu keputusan rilis, bukan sekadar warna merah di dasbor.
  5. Baris 76-84 (burn-rate): burn rate mengukur kecepatan pemakaian budget; ambang stale per jendela dihitung dari burn × fraksi budget × jendela, mengikuti pola multiwindow di Google SRE Workbook: Alerting on SLOs .

Pembacaan hasilnya menunjukkan kerja SLO yang sesungguhnya: freshness 93,3% dan completeness 96,7% sama-sama di bawah target, sehingga error budget keduanya habis terpakai. Konsekuensinya bukan hukuman, melainkan prioritas: rilis fitur Januari ditunda dan slot tersebut diberikan ke perbaikan keandalan — dua run yang bermasalah (3 dan 17 Desember) menjadi titik mulinya.

Anti-Pola SLO untuk Data #

Beberapa jebakan berulang di tim data. Pertama, SLO terlalu ketat: freshness 99,99% untuk batch harian hanya memberi toleransi 4,3 menit per bulan — mustahil dijangkau dan hanya melatih tim mengabaikan alarm. Kedua, SLI tak terukur: “data berkualitas tinggi” bukan indikator sampai ada definisi operasionalnya. Ketiga, SLO tanpa konsekuensi: budget habis tetapi rilis berisiko tetap jalan — angkanya ada, keputusannya tidak. Keempat, terlalu banyak SLI: bila sepuluh indikator dipantau sama kuat, tidak ada yang diprioritaskan saat semuanya merah bersamaan.

Dengan SLO, seluruh section Deployment dan Operasional kini menutup satu lingkaran: deployment yang berversi dan bisa diputar balik, insiden yang dikelola dengan proses, kapasitas yang terencana, biaya yang terukur, dan janji layanan yang dinyatakan dalam angka. Lingkaran berikutnya adalah efisiensi — pipeline yang sama selesai lebih cepat dengan sumber daya lebih hemat — dan perjalanan itu dimulai dari Chapter Mengukur Performa Pipeline di section Optimasi dan Performa.