- Nanit menggunakan AWS S3 dalam pipeline pemrosesan video untuk analisis tidur bayi, tetapi dengan ribuan upload per detik, biaya permintaan PutObject menjadi porsi terbesar dari total biaya
- Selain itu, karena batas retensi minimum 1 hari pada aturan S3 Lifecycle, untuk video yang sebenarnya diproses dalam 2 detik, mereka tetap harus membayar biaya penyimpanan 24 jam
- Untuk mengatasinya, mereka membangun N3, sistem penyimpanan in-memory berbasis Rust, dan menggunakan S3 hanya sebagai buffer overflow
- N3 sepenuhnya kompatibel dengan pipeline pemrosesan yang ada melalui SQS FIFO, sambil tetap mempertahankan jaminan urutan yang ketat dan keandalan
- Hasilnya, mereka menghemat sekitar $500.000 per tahun sekaligus mendapatkan arsitektur yang sederhana dan stabil
Latar belakang
Gambaran pipeline pemrosesan video
- Kamera Nanit merekam potongan video, meminta S3 presigned URL dari Camera Service, lalu mengunggah langsung ke S3
- AWS Lambda memublikasikan object key ke antrian SQS FIFO (dishard berdasarkan baby_uid), lalu pod pemrosesan video mengonsumsinya dari SQS, mengunduh dari S3, dan menjalankan inferensi status tidur
- Keuntungan dari pengaturan ini
- Landing di S3 + antrean SQS memisahkan upload kamera dan pemrosesan video, sehingga mencegah kehilangan video bahkan selama pemeliharaan atau downtime sementara
- Tidak perlu mengelola availability dan durability sendiri berkat S3
- SQS FIFO + group ID menjaga urutan per bayi, dan node pemrosesan bisa tetap sebagian besar stateless
- Aturan S3 Lifecycle menangani garbage collection sehingga tidak perlu melacak video yang sudah diproses
Mengapa perlu diubah
- Biaya PutObject mendominasi: video adalah objek berumur sangat pendek yang hanya singgah selama beberapa detik sebelum diproses, tetapi pada skala ribuan upload per detik, biaya permintaan per objek menjadi pendorong biaya terbesar
- Jika frekuensi chunking ditingkatkan (mengirim lebih banyak chunk kecil) demi menurunkan latensi, maka setiap chunk tambahan berarti satu permintaan PutObject lagi, sehingga biaya naik secara linear
- Penyimpanan menjadi pajak kedua: walaupun pemrosesan selesai dalam sekitar 2 detik, aturan penghapusan Lifecycle tetap mengenakan biaya penyimpanan sekitar 24 jam
- Mereka membutuhkan desain yang bisa menghindari biaya per objek di jalur normal sambil tetap menjaga keandalan dan jaminan urutan yang ketat, serta meminimalkan penyimpanan yang “membayar biaya saat menunggu”
Rencana
-
Prinsip desain
- Kesederhanaan melalui arsitektur: menghilangkan kompleksitas di tingkat desain, bukan lewat implementasi yang terlalu cerdas
- Ketepatan: menjadi pengganti penuh yang transparan bagi bagian pipeline lainnya
- Dioptimalkan untuk jalur normal: dirancang untuk kasus umum, dan menggunakan S3 sebagai jaring pengaman untuk edge case; karena algoritme pemrosesan tahan terhadap jeda sesekali, kesederhanaan diprioritaskan daripada membangun jaminan yang kompleks
-
Pendorong desain
- Objek berumur pendek: segmen hanya berada di area landing selama beberapa detik
- Urutan: sequencing ketat per bayi (tidak memproses data yang lebih baru lebih dulu)
- Throughput: ribuan upload per detik, 2-6 MB per segmen
- Keterbatasan klien: kamera memiliki jumlah retry terbatas, jadi tidak bisa mengandalkan retransmisi
- Operasional: harus bisa menoleransi backlog jutaan item selama pemeliharaan/scale-up
- Tanpa perubahan firmware: harus bekerja dengan kamera yang sudah ada
- Toleransi kehilangan: celah yang sangat kecil dapat diterima dan ditutupi oleh algoritme
- Biaya: menghindari biaya S3 per objek di jalur normal dan meminimalkan penyimpanan “membayar biaya saat menunggu”
Gambaran desain (jalur normal N3 + overflow S3)
-
Arsitektur
- N3 adalah area landing kustom yang menyimpan video di memori hanya selama waktu yang dibutuhkan untuk menguras pemrosesan (sekitar 2 detik), dan S3 hanya dipakai ketika N3 tidak mampu menangani beban
- Dua komponen
- N3-Proxy (stateless, antarmuka ganda)
- eksternal (terhubung ke internet): menerima upload kamera melalui presigned URL
- internal (privat): menerbitkan presigned URL ke Camera Service
- N3-Storage (stateful, khusus internal): menyimpan segmen yang diunggah di RAM dan mengantrekan ke SQS dengan download URL yang dapat dialamatkan ke pod
- Pod pemrosesan video mengonsumsi dari SQS FIFO dan mengunduh dari storage yang ditunjuk URL tersebut (N3 atau S3)
-
Alur normal (Happy Path)
- Kamera meminta upload URL dari Camera Service
- Camera Service meminta presigned URL dari API internal N3-Proxy
- Kamera mengunggah video ke endpoint eksternal N3-Proxy
- N3-Proxy meneruskannya ke N3-Storage
- N3-Storage menyimpan video di memori dan mengantrekan ke SQS dengan download URL yang menunjuk ke dirinya sendiri
- Pod pemrosesan mengunduh dari N3-Storage lalu memprosesnya
-
Fallback dua tingkat
- Tier 1: fallback tingkat proxy (per permintaan)
- Jika N3-Storage tidak bisa menerima upload karena tekanan memori, backlog pemrosesan, kegagalan pod, dan sebagainya, N3-Proxy akan mengunggah ke S3 atas nama kamera
- Kamera sudah telanjur menerima URL N3 sebelum kegagalan terdeteksi
- Tier 2: rerouting tingkat cluster (seluruh trafik)
- Jika N3-Proxy atau N3-Storage tidak sehat, Camera Service akan berhenti menerbitkan URL N3 dan langsung mengembalikan S3 presigned URL
- Seluruh trafik akan mengalir ke S3 sampai N3 pulih
-
Mengapa dipisah menjadi dua komponen
- Blast radius: jika storage crash, proxy masih bisa merutekan ke S3; jika proxy crash, hanya trafik node itu yang terdampak, seluruh cluster storage tetap aman
- Profil resource: proxy intensif CPU/jaringan (terminasi TLS), storage intensif memori (menahan video), sehingga membutuhkan tipe instance dan skala yang berbeda
- Keamanan: storage tidak pernah bersentuhan dengan internet
- Keamanan rollout: saat memperbarui proxy (stateless), storage (yang memegang data aktif) tidak disentuh
Validasi desain
-
Hal yang perlu divalidasi
- Kapasitas dan sizing: durasi upload nyata di berbagai jaringan klien, compute yang dibutuhkan, dan ukuran buffer upload
- Model storage: apakah semuanya bisa disimpan di RAM atau perlu disk
- Resiliensi: bagaimana melakukan load balancing dengan murah dan menangani node gagal
- Kebijakan operasional: kebutuhan GC, ekspektasi retry, dan apakah hapus saat GET sudah cukup
- Unknown unknowns: edge case apa yang akan muncul saat ide ini bertemu dunia nyata
-
Pendekatan 1: stress test sintetis
- Membangun load generator yang mendorong sistem hingga batasnya dengan berbagai concurrency, klien lambat, beban berkelanjutan, dan downtime pemrosesan
- Tujuan: menemukan titik batas, mengidentifikasi bottleneck tak terduga, dan mendapatkan baseline deterministik untuk perencanaan kapasitas
-
Pendekatan 2: PoC produksi (mode mirror)
- Uji sintetis tidak bisa mereplikasi perilaku kamera nyata: Wi‑Fi tidak stabil, berbagai versi firmware, dan kondisi jaringan yang tak terduga
- Mode mirror: n3-proxy lebih dulu menulis ke S3 (retensi produksi), lalu juga menulis ke N3-Storage PoC (terhubung ke SQS canary + pemroses video)
- Cohort target: berdasarkan versi firmware / daftar Baby-UID
- Data parity: membandingkan status tidur antara PoC dan produksi, lalu menyelidiki perbedaannya
- Observability: dashboard per jalur (N3 vs S3), kedalaman antrean, latensi/RPS, error budget, analisis egress
- Feature flag (menggunakan Unleash) sangat penting: memungkinkan peralihan cohort secara real time tanpa deploy, menguji irisan sempit (firmware lama, kamera dengan Wi‑Fi lemah), lalu langsung memulihkan jika ada masalah
-
Temuan
- Bottleneck: terminasi TLS menghabiskan sebagian besar CPU, dan AWS burstable networking mengalami throttling setelah kredit habis
- Storage khusus memori layak dijalankan: distribusi waktu upload dan concurrency nyata menunjukkan working set bisa disimpan aman di RAM dengan margin, jadi disk tidak diperlukan
- Overhead TCP timestamp: sekitar 85% dari total byte yang ditransmisikan adalah frame ACK; menonaktifkan TCP timestamp (
sysctl -w net.ipv4.tcp_timestamps=0) menghemat 12 byte per ACK
- Risiko: saat mengirim banyak byte pada soket yang sama, nomor urut bisa wrap sehingga paket tertunda bisa salah digabung dan menyebabkan korupsi
- Mitigasi: (1) soket baru untuk setiap upload, (2) mendaur ulang soket n3-proxy ↔ n3-storage setelah sekitar 1 GB transfer
- Kebocoran memori: setelah rilis awal, memori n3-proxy terus meningkat
- Profiling
jemalloc menunjukkan pertumbuhan pada buffer hyper BytesMut per koneksi
- Beberapa koneksi klien berhenti di tengah transfer dan tidak dibersihkan, membuat buffer tertinggal dan memori terus naik
- Perbaikan: membuat soket berumur pendek dan menerapkan batas waktu
- Menonaktifkan keep-alive: koneksi langsung ditutup setelah setiap upload selesai
- Memperketat timeout: menetapkan timeout header/soket untuk menghentikan upload yang macet dan membebaskan buffer
Storage
-
Storage in-memory
- Memulai dari jalur paling sederhana: storage in-memory untuk menghindari tuning I/O dan memakai struktur data yang intuitif
- Menyimpan video dengan
Arc<DashMap<Ulid, Bytes>>; setiap upload video menaikkan bytes_used, dan setiap download menghapus video lalu menurunkannya
- Mulai menolak upload saat kapasitas mencapai sekitar 80% atau lebih untuk menghindari OOM, dan memberi sinyal ke n3-proxy agar berhenti menandatangani upload URL
- Tersedia handle
control untuk menjeda upload dan garbage collection secara manual
-
Restart yang graceful
- Karena storage hanya ada di memori, perlu mencegah data aktif hilang saat restart
- Proses restart graceful
- Pod menerima
SIGTERM (StatefulSet rolling satu per satu)
- Pod menjadi Not Ready dan keluar dari Service (tidak ada upload baru)
- Tetap melayani download untuk video yang sudah telanjur diunggah
- Saat download berhenti (tidak ada pembacaan baru belakangan ini → pemrosesan sudah terkuras)
- Menunggu semua permintaan terbuka selesai
- Setelah restart, lanjut ke pod berikutnya
- Dalam kondisi normal, pod akan terkuras dalam beberapa detik
-
GC
- Menggunakan dua mekanisme pembersihan
- Hapus saat download: video dihapus tepat setelah diunduh; PoC menunjukkan nol re-download, dan karena pemroses video melakukan retry secara internal, tidak perlu menyimpan data atau melacak status “sudah diproses”
- TTL GC untuk yang tertinggal: hapus saat download tidak mencakup segmen yang dilewati pemroses (tidak diunduh → tidak dihapus)
- Ditambahkan TTL GC ringan: memindai DashMap in-memory secara berkala dan menghapus entri yang lebih tua dari ambang yang dapat dikonfigurasi (misalnya beberapa jam)
- Mode pemeliharaan: selama downtime pemrosesan terencana, GC bisa dijeda melalui kontrol internal agar video tidak terhapus saat konsumsi berhenti
Kesimpulan
-
Hasil utama
- Dengan menggunakan S3 sebagai buffer fallback dan N3 sebagai area landing utama, mereka berhasil menghemat sekitar $500.000 per tahun sambil menjaga sistem tetap sederhana dan andal
- Insight utama: kebanyakan keputusan “build vs buy” berfokus pada fitur, tetapi pada skala besar, faktor ekonomi mengubah perhitungannya
- Untuk objek berumur pendek (sekitar 2 detik dalam operasi normal), replikasi atau durability canggih tidak diperlukan; storage in-memory sederhana sudah cukup
- Saat pemrosesan tertunda atau pemeliharaan memperpanjang umur objek, barulah diperlukan jaminan keandalan S3
- Keunggulan kedua sisi: N3 menangani jalur normal secara efisien, S3 menyediakan durability saat objek perlu hidup lebih lama
- Jika ada masalah pada N3 (tekanan memori, pod crash, masalah cluster), upload akan fail over ke S3 dengan mulus
-
Faktor keberhasilan
- Mendefinisikan masalah dengan jelas sejak awal: kendala, asumsi, dan batasan mencegah scope creep
- Validasi dini lewat PoC mode mirror: menemukan bottleneck (TLS, throttling jaringan) dan memverifikasi asumsi sebelum commit
- Mencegah over-engineering dan backtracking
-
Kapan layak membangun hal seperti ini
- Pertimbangkan infrastruktur kustom saat ada skala yang cukup untuk menghasilkan penghematan biaya yang berarti, dan ketika kendala spesifik memungkinkan solusi sederhana
- Upaya engineering untuk membangun dan merawat sistem harus lebih kecil daripada biaya infrastruktur yang dihilangkan
- Dalam kasus Nanit, kebutuhan spesifiknya (penyimpanan sementara, toleransi kehilangan, fallback S3) memungkinkan mereka membangun sesuatu yang cukup sederhana sehingga biaya pemeliharaannya tetap rendah
- Jika kedua faktor itu tidak ada, tetaplah menggunakan managed service
- Apakah akan dilakukan lagi? Ya, sistem ini berjalan stabil di produksi, dan desain fallback memungkinkan mereka menghindari kompleksitas tanpa mengorbankan keandalan
Belum ada komentar.