Chapter Object Storage pada section Data Lake dan Lakehouse telah memperkenalkan semantik dasar layanan ini: object ditulis utuh, dikenal lewat key, dan diakses lewat operasi put, get, delete, dan list. Ketika data TokoKita pindah ke layanan cloud seperti Amazon S3, Google Cloud Storage, atau Azure Blob Storage, ada tiga mekanika tambahan yang langsung memengaruhi biaya dan desain pipeline: storage class yang menentukan harga simpan per gigabyte, lifecycle policy yang memindahkan dan menghapus object secara otomatis, serta model konsistensi yang menentukan perilaku pipeline setelah penulisan. Chapter ini membedah ketiganya lewat simulasi yang berjalan lokal. Seluruh demo memakai kamus Python murni sebagai pengganti layanan sungguhan, sehingga kita bisa mengamati perilakunya tanpa akun cloud.
Bucket, Key, dan Prefix #
Bucket adalah wadah paling atas milik satu proyek atau satu lingkungan; di Amazon S3 namanya harus unik secara global di seluruh region penyedia. Di dalam bucket, setiap object dikenal lewat key — string datar tanpa struktur direktori sungguhan. Tiga penyedia besar memakai pola URI yang serupa untuk merujuk object:
| Layanan | Contoh URI |
|---|---|
| Amazon S3 | s3://data-toko/raw/pesanan/2026-10-19.parquet |
| Google Cloud Storage | gs://data-toko/raw/pesanan/2026-10-19.parquet |
| Azure Blob Storage | abfs://[email protected]/raw/pesanan/2026-10-19.parquet |
Karakter / pada key hanyalah pemisah yang enak dibaca; layanan menampilkan ilusi folder hanya sebagai kenyamanan visual, sebagaimana sudah dibahas pada chapter Object Storage di section Data Lake dan Lakehouse. Operasi listing memakai prefix — filter startswith pada string key — sehingga konvensi penamaan seperti data/toko/2026/raw/pesanan/ sekaligus menjadi cara memilih subset data secara murah.
Storage Class: Menukar Harga dengan Waktu Akses #
Penyedia menawarkan beberapa storage class dengan kombinasi harga simpan dan waktu akses yang berbeda. Daftar kelas Amazon S3 berikut menggambarkan pola yang juga berlaku di penyedia lain:
| Storage class | Waktu akses | Syarat minimal | Harga simpan relatif |
|---|---|---|---|
| Standard | milidetik | tidak ada | 1× (basis) |
| Standard-IA | milidetik | objek 128 KB, retensi 30 hari | sekitar 0,5× |
| Glacier Instant Retrieval | milidetik | retensi 90 hari | sekitar 0,2× |
| Glacier Flexible Retrieval | menit hingga jam | retensi 90 hari | sekitar 0,15× |
| Deep Archive | sekitar 12 jam | retensi 180 hari | di bawah 0,05× |
Harga per gigabyte turun drastis di kelas dingin, tetapi dua biaya lain naik: retrieval (mengambil data dari Glacier dikenai biaya per GB dan tidak instan) dan penalti penghapusan awal bila object dihapus sebelum retensi minimal. Pedoman praktisnya: data yang dibaca ulang setiap minggu tetap di kelas panas, arsip yang hanya dibaca saat audit tahunan boleh turun ke Glacier atau Deep Archive. Rincian resmi setiap kelas tersedia di https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html .
Lifecycle Policy: Transisi dan Kedaluwarsa Otomatis #
Lifecycle policy adalah aturan yang menempel pada bucket: setelah object berumur N hari, layanan memindahkannya ke storage class lain atau menghapusnya. Aturan bisa difilter per prefix, sehingga data klik dan data pesanan TokoKita bisa punya jalur retensi berbeda dalam satu bucket. Simulasi berikut menerapkan tiga aturan pada key yang sudah kita kenal:
"""Simulasi lifecycle policy dan storage class object storage TokoKita.
Key disimpan sebagai string datar di satu kamus supaya terlihat jelas bahwa
"folder" hanyalah ilusi dari karakter '/' di dalam key.
"""
from datetime import date
HARI_INI = date(2026, 10, 20)
# Harga per GB-bulan (ilustrasi, basis S3 Standard USD 0.023/GB).
HARGA = {"STANDARD": 0.023, "STANDARD_IA": 0.0125,
"GLACIER_IR": 0.004, "GLACIER": 0.0036}
# key datar -> (ukuran GB, tanggal dibuat).
OBJEK = {
"data/toko/2026/raw/pesanan/2026-04-10.parquet": (24.0, date(2026, 4, 10)),
"data/toko/2026/raw/pesanan/2026-07-18.parquet": (22.0, date(2026, 7, 18)),
"data/toko/2026/raw/pesanan/2026-08-30.parquet": (25.0, date(2026, 8, 30)),
"data/toko/2026/raw/pesanan/2026-09-12.parquet": (23.0, date(2026, 9, 12)),
"data/toko/2026/raw/pesanan/2026-10-01.parquet": (24.0, date(2026, 10, 1)),
"data/toko/2026/raw/pesanan/2026-10-19.parquet": (24.0, date(2026, 10, 19)),
"data/toko/2026/raw/klik/2026-04-20.parquet": (70.0, date(2026, 4, 20)),
"data/toko/2026/raw/klik/2026-08-20.parquet": (76.0, date(2026, 8, 20)),
"data/toko/2026/raw/klik/2026-09-20.parquet": (79.0, date(2026, 9, 20)),
"data/toko/2026/raw/klik/2026-10-20.parquet": (84.0, date(2026, 10, 20)),
"data/toko/2026/curated/penjualan.parquet": (120.0, date(2026, 10, 15)),
"data/toko/2026/laporan/gmv_bulanan.csv": (0.2, date(2026, 10, 17)),
"log/etl/2026-10-20/jalankan.txt": (0.01, date(2026, 10, 20)),
"data/toko/2025/raw/pesanan/2025-03-10.parquet": (22.0, date(2025, 3, 10)),
}
# Lifecycle policy: aturan pertama yang prefix-nya cocok dengan key yang dipakai.
# Setiap aturan: (setelah_hari, aksi) dengan aksi = storage class atau "HAPUS".
POLICY = [
("data/toko/2026/raw/klik/", [(30, "GLACIER_IR")]),
("data/toko/2026/raw/pesanan/", [(30, "STANDARD_IA"), (90, "GLACIER"), (365, "HAPUS")]),
("data/toko/", [(365, "HAPUS")]),
]
def daftar_prefix(prefix: str) -> list:
return [k for k in sorted(OBJEK) if k.startswith(prefix)]
def umur(kunci: str) -> int:
return (HARI_INI - OBJEK[kunci][1]).days
def terapkan(kunci: str) -> str:
"""Kembalikan storage class akhir, atau 'HAPUS' bila kedaluwarsa."""
for prefix, aturan in POLICY:
if kunci.startswith(prefix):
for hari, aksi in aturan:
if aksi == "HAPUS" and umur(kunci) >= hari:
return "HAPUS"
kelas = "STANDARD"
for hari, aksi in aturan:
if aksi != "HAPUS" and umur(kunci) >= hari:
kelas = aksi
return kelas
return "STANDARD"
print("Semua key (namespace datar):")
for kunci in sorted(OBJEK):
print(" ", kunci)
print()
print("Prefix 'data/toko/2026/raw/pesanan/' :", len(daftar_prefix("data/toko/2026/raw/pesanan/")), "objek")
print("Prefix 'data/toko/2026/' :", len(daftar_prefix("data/toko/2026/")), "objek")
print("Prefix 'log/' :", len(daftar_prefix("log/")), "objek")
print()
print("Lifecycle policy per {:}:".format(HARI_INI.isoformat()))
print()
print("{:<46}{:>5} {}".format("Key", "Umur", "Hasil"))
hasil = {}
for kunci in sorted(OBJEK):
hasil[kunci] = terapkan(kunci)
print("{:<46}{:>5} {}".format(kunci, umur(kunci), hasil[kunci]))
sebelum = sum(ukuran * HARGA["STANDARD"] for ukuran, _ in OBJEK.values())
sesudah = sum(ukuran * HARGA[hasil[k]]
for k, (ukuran, _) in OBJEK.items() if hasil[k] != "HAPUS")
print()
print("Biaya simpan sebulan (semua STANDARD vs setelah policy):")
print(" Sebelum : ${:.2f}".format(sebelum))
print(" Sesudah : ${:.2f}".format(sesudah))
print(" Hemat : {:.1f}%".format((1 - sesudah / sebelum) * 100))Output yang Dihasilkan:
Semua key (namespace datar):
data/toko/2025/raw/pesanan/2025-03-10.parquet
data/toko/2026/curated/penjualan.parquet
data/toko/2026/laporan/gmv_bulanan.csv
data/toko/2026/raw/klik/2026-04-20.parquet
data/toko/2026/raw/klik/2026-08-20.parquet
data/toko/2026/raw/klik/2026-09-20.parquet
data/toko/2026/raw/klik/2026-10-20.parquet
data/toko/2026/raw/pesanan/2026-04-10.parquet
data/toko/2026/raw/pesanan/2026-07-18.parquet
data/toko/2026/raw/pesanan/2026-08-30.parquet
data/toko/2026/raw/pesanan/2026-09-12.parquet
data/toko/2026/raw/pesanan/2026-10-01.parquet
data/toko/2026/raw/pesanan/2026-10-19.parquet
log/etl/2026-10-20/jalankan.txt
Prefix 'data/toko/2026/raw/pesanan/' : 6 objek
Prefix 'data/toko/2026/' : 12 objek
Prefix 'log/' : 1 objek
Lifecycle policy per 2026-10-20:
Key Umur Hasil
data/toko/2025/raw/pesanan/2025-03-10.parquet 589 HAPUS
data/toko/2026/curated/penjualan.parquet 5 STANDARD
data/toko/2026/laporan/gmv_bulanan.csv 3 STANDARD
data/toko/2026/raw/klik/2026-04-20.parquet 183 GLACIER_IR
data/toko/2026/raw/klik/2026-08-20.parquet 61 GLACIER_IR
data/toko/2026/raw/klik/2026-09-20.parquet 30 GLACIER_IR
data/toko/2026/raw/klik/2026-10-20.parquet 0 STANDARD
data/toko/2026/raw/pesanan/2026-04-10.parquet 193 GLACIER
data/toko/2026/raw/pesanan/2026-07-18.parquet 94 GLACIER
data/toko/2026/raw/pesanan/2026-08-30.parquet 51 STANDARD_IA
data/toko/2026/raw/pesanan/2026-09-12.parquet 38 STANDARD_IA
data/toko/2026/raw/pesanan/2026-10-01.parquet 19 STANDARD
data/toko/2026/raw/pesanan/2026-10-19.parquet 1 STANDARD
log/etl/2026-10-20/jalankan.txt 0 STANDARD
Biaya simpan sebulan (semua STANDARD vs setelah policy):
Sebelum : $13.64
Sesudah : $7.47
Hemat : 45.3%Mari kita telaah baris-baris kode di atas:
- Baris 11-12 (
HARGA): harga per GB-bulan untuk empat kelas — angka ilustratif dengan basis Standard USD 0,023/GB. - Baris 15-30 (
OBJEK): key datar dipetakan ke ukuran dan tanggal dibuat; tidak ada struktur direktori, hanya string dalam satu kamus. - Baris 34-38 (
POLICY): tiga grup aturan dengan filter prefix — klik turun ke Glacier Instant Retrieval setelah 30 hari, pesanan melalui tiga tahap, sisanya mengikuti aturan umumdata/toko/. - Baris 46-58 (
terapkan): aturanHAPUSdiperiksa lebih dulu; bila umur belum menyentuh ambang hapus, storage class diambil dari transisi dengan ambang terbesar yang sudah terlampaui. - Baris 76-78 (
sebelum,sesudah): biaya sebelum menghitung semua object sebagai Standard; biaya sesudah memakai kelas hasil policy dan membuang object yang dihapus.
Bacaan hasilnya: data klik berumur 30 hari atau lebih turun ke Glacier Instant Retrieval sehingga analitik mingguan tetap bisa membacanya dalam hitungan milidetik; pesanan lama turun bertahap ke Glacier; dan arsip 2025 berumur 589 hari terhapus oleh aturan umum. Total tagihan simpan menyusut 45,3% tanpa satu pun object dipindahkan manual.
Konsistensi Baca-Tulis #
Sejak Desember 2020 Amazon S3 memberikan strong consistency: hasil PUT dan DELETE langsung terlihat oleh permintaan baca berikutnya tanpa masa tunggu replikasi. Google Cloud Storage juga konsisten kuat untuk penulisan object. Bagi pipeline, perilaku ini menyederhanakan orchestration — langkah extract yang menulis raw/pesanan/2026-10-20.parquet bisa langsung diikuti langkah transform yang membacanya, tanpa perulangan menunggu object muncul. Catatan pentingnya: konsistensi berlaku per object, bukan untuk operasi LIST sebagai katalog tabel — pencatatan tabel yang andal tetap membutuhkan table format seperti yang dibahas pada chapter Apache Iceberg di section Data Lake dan Lakehouse.
Dengan mekanika bucket, storage class, dan lifecycle yang dikuasai, TokoKita kini punya lapisan penyimpanan yang murah dan terkelola otomatis. Pertanyaan berikutnya: bagaimana mengolah data tersebut tanpa mengikat proses komputasi ke satu mesin penyimpanan — itu bahasan chapter Compute dan Storage.