Konteks dan Deskripsi Soal
Rancang layanan obrolan teks dengan 50 juta pengguna aktif harian, 5 juta koneksi konkuren pada jam sibuk, dan 2 miliar pesan per hari. Sistem ini mendukung percakapan langsung (direct messages), grup dengan maksimal 200 anggota, banyak perangkat per pengguna, sinkronisasi pesan tertinggal secara luring (offline catch-up), tanda terima pengiriman dan pembacaan, status kehadiran (presence), serta indikator pengetikan. Lampiran, saluran publik, penulisan aktif-aktif lintas wilayah, pencarian teks lengkap, dan enkripsi ujung-ke-ujung (end-to-end encryption) adalah cakupan lanjutan.
Layanan hanya memberikan konfirmasi penerimaan (acknowledgment) pengiriman setelah pesan disimpan secara persisten (durably stored). Sistem tidak boleh kehilangan pesan yang telah dikonfirmasi secara diam-diam. Untuk penerima yang sudah daring di bawah beban normal, p99 dari penerimaan persisten hingga pesan tiba di perangkat yang terhubung harus di bawah satu detik. Lapisan transport dapat mengirim ulang pesan, sehingga klien harus melihat satu pesan logis setelah deduplikasi. Pengurutan pesan diwajibkan di dalam satu percakapan, bukan di seluruh percakapan yang tidak terkait.
Tiga panduan desain sistem publik yang diterbitkan atau diperbarui pada tahun 2026 menyajikan obrolan sebagai soal wawancara langsung dan berulang kali menguji koneksi persisten, perutean koneksi, pengurutan per percakapan, sinkronisasi luring, tanda terima, dan kehadiran. Standar WebSocket mendefinisikan mekanisme transport dua arah dan frame kontrol, sedangkan spesifikasi klien-server Matrix menyediakan contoh tingkat produksi untuk ID transaksi klien, token sinkronisasi inkremental, tanda terima pembacaan, dan peristiwa pengetikan yang bersifat efemeral. Sumber-sumber tersebut menetapkan bahwa topik dan batasan kegagalannya relevan dan memiliki dasar teknis yang kuat; skala dan SLO dalam soal ini adalah input wawancara fiktif.
Hal-Hal yang Dievaluasi oleh Pewawancara
Sinyal pertama adalah presisi kontrak data. "Terkirim" (sent), "diterima" (delivered), dan "dibaca" (read) adalah fakta yang berbeda. Konfirmasi dari server berarti penerimaan yang persisten. Tanda terima pengiriman berarti perangkat penerima yang dipilih telah menerima pesan. Tanda terima pembacaan membutuhkan aturan produk yang eksplisit bahwa pengguna benar-benar membuka dan menampilkan percakapan tersebut. Memperlakukan ketiganya sebagai satu status yang sama akan menciptakan klaim keandalan palsu dan jumlah pesan belum dibaca yang salah.
Sinyal kedua adalah apakah kandidat menghindari janji pengiriman tepat satu kali (exactly-once) secara end-to-end. Klien mungkin mengalami waktu habis (timeout) setelah server melakukan commit tetapi sebelum konfirmasi tiba. Gateway dapat mengulangi pengiriman setelah rekoneksi. Kontrak praktisnya adalah transport setidaknya satu kali (at-least-once) yang dikombinasikan dengan ID pesan klien yang stabil, catatan idempotensi server, dan deduplikasi klien berdasarkan ID pesan server.
Sinyal ketiga adalah batasan pengurutan. Urutan global akan membuat percakapan yang tidak saling terkait berjalan lambat karena antrean serial. Kontrak produk yang berguna memberikan setiap pesan yang diterima sebuah nomor urut monotonik (monotonic sequence) di dalam percakapannya. Oleh karena itu, semua penulisan untuk satu percakapan diteruskan ke satu pemilik saat ini atau sekuenser. Ini menjaga urutan lokal sembari memungkinkan percakapan yang berbeda untuk melakukan penskalaan secara independen, serta mengekspos batas sebenarnya dari percakapan yang sangat aktif (hot conversation).
Sinyal keempat adalah pemisahan status persisten dan efemeral. Pesan, interval keanggotaan, dan kursor baca harus bertahan dari kegagalan. Status kehadiran dan pengetikan dapat kedaluwarsa dan diabaikan jika perlu. Membiarkan lalu lintas pengetikan berbagi log pesan persisten akan membuang-buang penyimpanan dan lonjakan data sementara dapat menunda pengiriman pesan asli.
Sinyal terakhir adalah pola pikir pemulihan kegagalan. Jawaban harus mencakup konfirmasi pengiriman yang hilang, kegagalan gateway, badai rekoneksi (reconnect storms), pemilik percakapan yang basi (stale), perangkat yang lambat, perubahan keanggotaan, duplikasi fan-out, dan grup yang sangat aktif. Angka kapasitas harus diturunkan dari parameter soal lalu dikalibrasi dengan uji beban, bukan disajikan sebagai batas server universal.
Pertanyaan yang Perlu Diklarifikasi Sebelum Menjawab
- Konten dan jenis percakapan apa saja yang masuk dalam cakupan? Jawaban ini menangani teks, percakapan langsung, dan grup hingga 200 anggota. Lampiran menggunakan penyimpanan objek dan pesan metadata; saluran publik membutuhkan strategi fan-out dan riwayat yang berbeda.
- Apa arti dari setiap tanda terima? Diterima oleh server, diterima oleh perangkat, dan dibaca oleh pengguna adalah fakta monotonik yang terpisah. "Dibaca" hanya terjadi ketika klien menampilkan percakapan yang relevan, bukan saat hanya menerima notifikasi push.
- Pengurutan seperti apa yang dibutuhkan? Soal membutuhkan urutan yang stabil per percakapan. Ini tidak menjanjikan bahwa jam perangkat pengguna menentukan urutan atau bahwa dua percakapan berbeda berbagi satu urutan logis yang sama.
- Bisakah pengguna mengirim dari beberapa perangkat? Ya. Setiap perangkat memiliki koneksi terautentikasi dan kursor sinkronisasi sendiri; percobaan ulang pengirim menggunakan ID pesan klien yang sama.
- Apa yang terjadi ketika keanggotaan berubah? Otorisasi diperiksa saat penerimaan pesan. Produk harus mendefinisikan apakah anggota baru dapat membaca riwayat lama dan apa yang disimpan secara lokal oleh anggota yang dihapus. Desain ini menyimpan interval keanggotaan berdasarkan urutan percakapan.
- Berapa lama pesan dan catatan idempotensi disimpan? Retensi pesan adalah keputusan produk dan kepatuhan hukum. Pemetaan idempotensi harus mencakup jendela percobaan ulang klien maksimum; menghapusnya terlalu dini dapat menimbulkan duplikasi pesan.
- Wilayah (region) mana saja yang digunakan? Jawaban dasar menetapkan wilayah asal (home region) untuk setiap percakapan dan melakukan failover secara terencana. Replika yang dapat ditulis secara bersamaan di banyak wilayah membutuhkan model resolusi konflik dan pengurutan yang jauh lebih rumit.
- Apa saja yang berada di luar target satu detik? Tampilan notifikasi push saat luring, sinkronisasi saat terhubung kembali, waktu baca pengguna, dan pemulihan bencana lintas wilayah diukur secara terpisah dari pengiriman langsung ke perangkat yang sudah terhubung.
Kerangka Jawaban 30 Detik
"Saya akan memisahkan manajemen koneksi dari jalur pesan persisten. Klien mengirim pesan melalui WebSocket terautentikasi dengan ID pesan klien yang stabil. Layanan dengan perutean berbasis percakapan memeriksa ulang keanggotaan, menetapkan urutan per percakapan berikutnya, menyimpan pesan dan hasil idempotensi secara atomik, dan baru kemudian mengirimkan konfirmasi. Pekerja fan-out asinkron mencari perangkat penerima yang aktif dan mengirimkan pesan ke gateway mereka; perangkat yang luring atau gagal terkirim dipulihkan melalui API sinkronisasi berbasis kursor. Kursor pengiriman dan pembacaan bertambah secara monotonik, sementara status kehadiran dan pengetikan menggunakan jalur efemeral yang dapat kedaluwarsa. Transportasi data bersifat at-least-once, sehingga server dan klien melakukan deduplikasi. Pada skala yang disebutkan, saya akan merencanakan rata-rata sekitar 23.000 dan puncak 230.000 pengiriman per detik, sekitar 2 TB data pesan logis per hari dengan asumsi 1 KB per pesan, dan melakukan uji beban pada koneksi persisten, grup dengan lalu lintas tinggi, badai rekoneksi, percobaan ulang, perubahan keanggotaan, serta failover pemilik terhadap SLO."
Pembahasan Mendalam Langkah demi Langkah
Langkah 1: Tetapkan kontrak produk dan hitung estimasi kapasitas awal.
Dua miliar pesan per hari menghasilkan rata-rata sekitar 23.148 pengiriman per detik. Jika beban puncak adalah sepuluh kali lipat dari rata-rata, perencanaan kapasitas masuk (ingress) dimulai dari sekitar 230.000 pengiriman per detik. Dengan asumsi 1 KB per pesan tersimpan termasuk metadata biasa dan indeks, volume penulisan logis adalah sekitar 2 TB per hari; tiga replika menghasilkan sekitar 6 TB per hari sebelum kompaksi, cadangan sistem berkas, pencadangan (backup), dan penulisan tanda terima. Ini adalah asumsi perencanaan, bukan batasan perangkat keras yang terukur.
Lima juta koneksi konkuren mendominasi perencanaan gateway. Misalkan uji beban pada instans yang dipilih, konfigurasi TLS, interval heartbeat, dan target p99 membuktikan bahwa satu gateway mampu menangani 50.000 koneksi yang sehat. Kebutuhan minimum absolut adalah 100 gateway. Mengoperasikannya pada 70% dari batas terukur tersebut membutuhkan sekitar 143, dibulatkan menjadi 150 gateway, ditambah cadangan untuk mengantisipasi kegagalan zona (zone failure). Pengujian harus mencakup pengiriman pesan, rekoneksi, dan klien yang lambat; menghitung soket menganggur saja akan menyesatkan.
Langkah 2: Tentukan protokol klien dan identitasnya.
Setiap perangkat membuka WebSocket yang terautentikasi ke gateway regional. Gateway memvalidasi origin jika berlaku, membatasi ukuran frame dan kecepatan pengiriman, memperbarui otorisasi, dan menggunakan ping/pong ditambah lease untuk mendeteksi koneksi yang mati. RFC 6455 menyediakan mekanisme koneksi teknis; protokol aplikasi tetap menangani autentikasi, konfirmasi, pengurutan, percobaan ulang, dan backpressure.
Frame utama dapat dinyatakan sebagai:
SEND {
conversation_id, client_message_id, body, client_sent_at
}
ACK {
client_message_id, message_id, conversation_seq, accepted_at
}
MESSAGE {
conversation_id, message_id, conversation_seq, sender_id, body, accepted_at
}
SYNC {
device_sync_cursor, limit
}client_message_id dibuat sekali dan digunakan kembali untuk setiap percobaan ulang dari perangkat pengirim tersebut. message_id adalah identitas server yang digunakan untuk deduplikasi di sisi penerima. conversation_seq adalah urutan tampilan dan sinkronisasi di dalam satu percakapan. accepted_at adalah waktu server untuk keperluan diagnostik, bukan otoritas pengurutan pesan.
Langkah 3: Lakukan commit sekali per pengiriman logis sebelum memberikan konfirmasi.
Gateway meneruskan SEND ke layanan obrolan. Layanan mengambil shard percakapan saat ini, mengautentikasi pengirim, memverifikasi keanggotaan pada versi keanggotaan terkini, memvalidasi ukuran, dan menerapkan batas laju per pengguna dan per percakapan. Pemilik shard menyusun penulisan yang diterima untuk percakapan tersebut secara serial.
Dalam satu transaksi persisten, layanan mengalokasikan urutan berikutnya, menulis pesan, dan menulis pemetaan unik seperti (sender_device_id, client_message_id) → message_id. Permintaan duplikat akan mengembalikan hasil sebelumnya, bukan menambahkan pesan baru. Hanya data yang berhasil di-commit secara kuorum yang akan menerima ACK. Jika konfirmasi hilang di jaringan, pengiriman ulang oleh klien tetap aman. Jika penyimpanan gagal sebelum commit, tidak ada konfirmasi yang dikirimkan.
Pesan dipartisi berdasarkan conversation_id dan diurutkan berdasarkan conversation_seq. Perubahan keanggotaan juga diurutkan, atau merujuk ke batas urutan yang efektif, sehingga otorisasi tidak hanya bergantung pada cache anggota saat ini yang bersifat konsisten pada akhirnya (eventually consistent). Epoch pemilik yang dipagari (fenced owner epoch) mencegah pemilik lama menerima penulisan setelah proses failover terjadi.
Langkah 4: Lakukan fan-out setelah commit persisten.
Log yang telah di-commit menerbitkan tugas pengiriman. Direktori koneksi memetakan (user_id, device_id) ke gateway dan lease koneksi. Untuk percakapan langsung atau grup dengan maksimal 200 anggota, pekerja fan-out memuat snapshot keanggotaan yang berlaku pada urutan pesan tersebut, mengelompokkan perangkat yang sedang online berdasarkan gateway, dan mengirimkan perintah gateway secara batch. Isi pesan disimpan sekali; fan-out hanya membawa referensi atau event ringkas alih-alih menduplikasi seluruh isi pesan persisten per penerima.
Gateway menempatkan event tersebut ke dalam antrean keluar terikat (bounded outbound queue) milik masing-masing koneksi. Perangkat mengembalikan kursor pengiriman yang monotonik setelah menerima event tersebut secara lokal. Jika suatu koneksi lambat, gateway akan berhenti melakukan buffering tanpa batas, menandainya untuk sinkronisasi ulang, dan menutup koneksi dengan alasan aplikasi. Log persisten tetap menjadi sumber kebenaran data, sehingga membatalkan pengiriman langsung di memori tidak akan menghilangkan pesan.
Untuk pengguna yang sedang luring, sistem dapat mengirimkan notifikasi push seluler yang aman bagi privasi. Notifikasi push hanyalah mekanisme untuk membangunkan perangkat, bukan media penyimpanan pesan atau bukti pengiriman. Saat aplikasi dibuka, klien mengautentikasi dan melakukan sinkronisasi data dari layanan persisten.
Langkah 5: Buat mekanisme rekoneksi dan sinkronisasi multi-perangkat secara eksplisit.
Setiap perangkat menyimpan device_sync_cursor yang bersifat opak. GET /sync?after=cursor atau frame yang setara mengembalikan delta percakapan yang terurut, perubahan keanggotaan, delta tanda terima, dan kursor baru. Server tidak boleh memajukan kursor melampaui event yang dihilangkan dari respons. Jika kursor kedaluwarsa atau jarak perbedaannya terlalu besar, server mengembalikan snapshot terbatas ditambah token kelanjutan, alih-alih mencoba memutar ulang data tanpa batas pada satu koneksi.
Pengiriman langsung dan sinkronisasi dapat saling tumpang tindih, sehingga klien menggabungkan pesan berdasarkan message_id dan mengurutkannya berdasarkan (conversation_id, conversation_seq). Perangkat baru akan menerima riwayat yang diizinkan oleh kebijakan dan membentuk kursornya sendiri. Status pembacaan berada pada tingkat pengguna dan bersifat monotonik per percakapan; status pengiriman dapat tetap dicatat per perangkat. Aturan agregasi harus menyatakan apakah "delivered" berarti telah sampai ke salah satu perangkat atau ke setiap perangkat yang aktif.
Langkah 6: Jaga tanda terima, kehadiran, dan pengetikan tetap akurat.
Pembaruan status baca membawa conversation_seq tertinggi yang telah ditampilkan; server menggunakan nilai maksimum kondisional sehingga permintaan yang datang terlambat tidak dapat memundurkan kursor. Model tanda terima dalam spesifikasi Matrix mengilustrasikan mengapa status baca adalah sebuah delta yang menggantikan posisi lama dan mengapa sekadar menerima pesan bukanlah bukti yang cukup bahwa pengguna telah melihat isinya.
Status kehadiran dan pengetikan mengikuti jalur yang berbeda. Gateway memperbarui lease kehadiran yang berdurasi pendek secara berkala. Peristiwa pengetikan diotorisasi, dibatasi lajunya, dicakupkan ke satu percakapan, digabungkan, dan kedaluwarsa setelah beberapa detik. Data ini dapat dibuang saat sistem kelebihan beban dan tidak pernah dimasukkan ke dalam log pesan persisten. Fitur "Terakhir dilihat" (Last seen) memerlukan kebijakan privasi yang jelas dan pembaruan berkala beresolusi rendah agar lalu lintas heartbeat tidak membebani database dengan operasi penulisan.
Langkah 7: Rancang jalur penanganan kegagalan sebelum mengklaim keandalan.
ACKhilang: klien mencoba lagi denganclient_message_idyang sama; server mengembalikan hasil yang sudah tersimpan sebelumnya.- Gateway crash: perangkat terhubung kembali dengan jitter dan melanjutkan dari kursor; lease koneksi kedaluwarsa.
- Pekerja fan-out crash: tugas pengiriman persisten dicoba kembali; gateway dan klien melakukan deduplikasi.
- Pemilik percakapan crash: epoch baru dan status kuorum memilih pengganti; pemilik lama dipagari (fenced).
- Perangkat lambat: antrean terikat memicu sinkronisasi ulang daripada menghabiskan memori tanpa batas.
- Percakapan dengan lalu lintas tinggi: satu sekuenser membatasi laju penulisan. Lakukan batching commit, isolasi shard yang sibuk, dan terapkan batas laju produk; menambahkan partisi hash biasa tidak dapat memparalelkan satu urutan yang ketat.
- Race condition keanggotaan: urutkan perubahan keanggotaan bersamaan dengan pesan dan lakukan otorisasi terhadap interval yang berlaku.
- Pemadaman wilayah (regional outage): rute percakapan dialihkan ke replika yang dipromosikan hanya setelah memagari (fencing) wilayah asal yang lama; waktu pemulihan dan kemungkinan hilangnya data yang belum dikonfirmasi terpisah dari jaminan tanpa kehilangan untuk pesan yang telah dikonfirmasi.
Upaya koneksi ulang menggunakan exponential backoff dengan variasi acak (jitter) dan kontrol penerimaan (admission control). Jika tidak, restart gateway regional dapat mengubah lima juta klien yang sehat menjadi badai handshake yang mencegah pemulihan sistem.
Langkah 8: Buktikan invarian dengan pengujian berlapis dan observabilitas.
Pengujian berbasis properti (property tests) menghasilkan percobaan ulang, pengiriman yang tidak berurutan, perubahan keanggotaan, dan pergantian epoch pemilik, lalu memvalidasi satu pesan logis per ID klien, nomor urut percakapan yang unik, kursor monotonik, dan tidak adanya pesan tidak sah setelah batas penghapusan anggota. Pengujian integrasi mematikan proses sebelum commit, setelah commit tetapi sebelum konfirmasi, dan selama proses fan-out. Pengujian chaos mematikan gateway, pekerja, pemilik shard, zona, dan partisi direktori koneksi secara acak.
Uji beban memodelkan lima juta koneksi jangka panjang, 230.000 pengiriman puncak per detik, fan-out grup, penerima yang lambat, dan koneksi ulang massal. Amati latensi penerimaan, latensi pengiriman langsung, jeda sinkronisasi, tingkat duplikasi sebelum dan sesudah deduplikasi klien, celah nomor urut, saturasi shard sibuk, ukuran byte antrean keluar, penerimaan koneksi ulang, dan penolakan pembacaan yang tidak sah. Peluncuran canary hanya diperluas jika invarian persistensi dan otorisasi tetap terjaga dengan baik.
Contoh Jawaban Berkualitas Tinggi
"Pertama-tama saya akan mendefinisikan tiga hasil yang berbeda: penerimaan persisten oleh server, pengiriman ke perangkat, dan pembacaan oleh pengguna. Layanan hanya mengirimkan konfirmasi setelah kuorum menyimpan pesan. Pengiriman jaringan tetap berprinsip at-least-once, sehingga ID pesan klien yang dibuat oleh pengirim membuat percobaan ulang bersifat idempoten dan penerima melakukan deduplikasi berdasarkan ID pesan server.
Klien terhubung ke gateway WebSocket regional. Direktori koneksi memberi tahu pekerja fan-out gateway mana yang menangani masing-masing perangkat pengguna, tetapi gateway tidak menyimpan riwayat percakapan. Layanan obrolan merutekan setiap penulisan berdasarkan ID percakapan ke pemilik saat ini yang telah dipagari (fenced). Pemilik tersebut memeriksa ulang keanggotaan, mengalokasikan urutan per percakapan, dan secara atomik menyimpan pesan beserta hasil idempotensinya. Percakapan yang tidak terkait dieksekusi secara paralel; satu percakapan yang sangat sibuk tetap menjadi bottleneck serial yang disengaja.
Setelah commit, pekerja melakukan fan-out ke perangkat yang sedang online. Pengiriman yang gagal atau tertunda saat luring dipulihkan melalui API sinkronisasi berbasis kursor. Jalur pengiriman langsung dan pemutaran ulang dapat tumpang tindih, sehingga ID pesan mengatasi duplikasi dan nomor urut percakapan memulihkan urutan lokal. Kursor baca bertambah berdasarkan urutan maksimum. Kehadiran dan pengetikan menggunakan data efemeral dengan pendekatan best-effort dan tidak boleh menghambat alur pesan persisten.
Pada volume 2 miliar pesan per hari, rata-rata lalu lintas masuk adalah sekitar 23.000 per detik; saya akan merencanakan sekitar 230.000 pada beban puncak sepuluh kali lipat. Dengan asumsi 1 KB per pesan, penyimpanan pesan logis sekitar 2 TB per hari dan 6 TB dengan tiga replika. Untuk lima juta koneksi, batas gateway terukur 50.000 koneksi membutuhkan 100 node dasar atau sekitar 150 node pada utilisasi 70% sebelum memperhitungkan cadangan kegagalan zona.
Saya akan memvalidasi skenario konfirmasi yang hilang, kegagalan gateway dan pemilik shard, epoch yang basi, duplikasi fan-out, perangkat lambat, race condition keanggotaan, grup bervolume tinggi, dan badai koneksi ulang. Syarat kelayakan rilis adalah tidak ada pesan yang terkonfirmasi yang hilang, tidak ada duplikasi pesan logis setelah deduplikasi, tidak ada regresi nomor urut, tidak ada akses di luar interval keanggotaan, dan tercapainya target p99 pengiriman langsung di bawah beban yang representatif."
Kesalahan Umum
- Kesalahan: Menganggap penerimaan server, pengiriman ke perangkat, dan pembacaan sebagai satu status yang sama → Konsekuensi: Metrik keandalan dan jumlah pesan belum dibaca menjadi tidak dapat diverifikasi → Solusi: Definisikan status monotonik dan pemilik tanggung jawab yang terpisah.
- Kesalahan: Menjanjikan pengiriman exactly-once melalui jaringan → Konsekuensi: Konfirmasi yang hilang atau koneksi ulang menghasilkan duplikasi yang tidak terjelaskan → Solusi: Gunakan transportasi at-least-once dengan ID pengiriman stabil dan deduplikasi.
- Kesalahan: Mengurutkan semua pesan secara global → Konsekuensi: Percakapan yang tidak saling terkait berbagi satu titik bottleneck yang sama → Solusi: Tetapkan urutan hanya di dalam masing-masing percakapan.
- Kesalahan: Mengirim konfirmasi sebelum commit persisten selesai → Konsekuensi: Kegagalan proses atau zona dapat menghapus pesan yang telah diterima secara diam-diam → Solusi: Berikan konfirmasi hanya setelah data berhasil di-commit secara kuorum.
- Kesalahan: Menyimpan riwayat pesan di gateway WebSocket → Konsekuensi: Perpindahan koneksi menjadi perpindahan data dan hilangnya gateway mengancam riwayat pesan → Solusi: Buat gateway tetap stateless di luar status koneksi yang terikat.
- Kesalahan: Melakukan buffering tanpa batas untuk perangkat yang lambat → Konsekuensi: Satu klien lambat dapat menghabiskan memori gateway → Solusi: Batasi antrean, putuskan koneksi, dan sinkronkan ulang dari penyimpanan persisten.
- Kesalahan: Memasukkan data kehadiran dan pengetikan ke dalam log persisten → Konsekuensi: Lalu lintas sementara berbiaya tinggi dapat menunda pengiriman pesan penting → Solusi: Gunakan jalur data ber-TTL yang terotorisasi, dibatasi lajunya, dan bersifat best-effort.
- Kesalahan: Mengotorisasi hanya dari cache anggota saat ini yang sudah basi → Konsekuensi: Pengguna yang telah dikeluarkan dapat mengirim atau menerima pesan akibat race condition → Solusi: Urutkan batas keanggotaan dan pagar (fence) penerimaan pesan terhadapnya.
- Kesalahan: Menentukan kapasitas gateway hanya berdasarkan jumlah soket menganggur → Konsekuensi: Beban TLS, heartbeat, fan-out, dan rekoneksi merusak perencanaan kapasitas → Solusi: Lakukan benchmark beban kerja penuh pada target p99 dan sediakan ruang cadangan untuk kegagalan.
Pertanyaan Lanjutan dan Jawabannya
Pertanyaan Lanjutan 1: Bagaimana Anda menambahkan enkripsi ujung-ke-ujung (end-to-end encryption)?
Enkripsi isi pesan di perangkat pengirim dan simpan hanya teks tersandi (ciphertext) beserta metadata perutean yang diperlukan. Setiap perangkat memerlukan kunci identitas, daftar perangkat yang ditandatangani, dan mekanisme distribusi kunci percakapan; penambahan atau penghapusan perangkat atau anggota akan merotasi atau mendistribusikan ulang kunci sesuai kebijakan. Server tetap dapat mengurutkan dan merutekan ciphertext, tetapi pencarian di sisi server, moderasi konten, pemulihan data, pratinjau tautan, dan penanganan penyalahgunaan menjadi sangat terbatas. Metadata pengiriman, daftar peserta, waktu, dan ukuran pesan mungkin tetap dapat diamati, sehingga enkripsi tidak menghilangkan kebutuhan perlindungan privasi metadata.
Pertanyaan Lanjutan 2: Bisakah satu grup yang sangat aktif dibagi ke banyak sekuenser?
Tidak bisa jika ingin mempertahankan satu urutan yang berurutan ketat tanpa adanya otoritas pengurutan lain. Membagi percakapan ke lebih banyak shard melalui hashing tetap menyisakan kebutuhan titik penggabungan atau konsensus. Solusi awalnya adalah melakukan batch commit dan mengisolasi shard tersebut, kemudian menerapkan batas laju per percakapan. Jika produk dapat menerima urutan parsial, lakukan partisi berdasarkan utas (thread) atau pengirim dan tampilkan hubungan kausalitasnya, tetapi ini mengubah kontrak produk dan meningkatkan kompleksitas pada klien.
Pertanyaan Lanjutan 3: Pengirim melihat status ACK, tetapi penerima luring selama seminggu. Apakah pesan tersebut terkirim?
Pesan tersebut telah diterima oleh server, tetapi belum terkirim ke perangkat tersebut. Pesan persisten tetap tersedia berdasarkan kebijakan retensi, dan notifikasi push dapat membangunkan perangkat. Saat terhubung kembali, perangkat menyinkronkan data dari kursornya lalu melaporkan status penerimaan. UI produk dan metrik sistem harus memisahkan antara diterima-server, terkirim-ke-salah-satu-perangkat, terkirim-ke-semua-perangkat-aktif, dan dibaca.
Pertanyaan Lanjutan 4: Bagaimana pengeditan dan penghapusan pesan berinteraksi dengan pengurutan?
Representasikan pengeditan dan penghapusan sebagai event baru yang terurut dan merujuk ke pesan asli, alih-alih mengubah riwayat pesan lama secara langsung. Klien akan menggabungkan aliran event tersebut ke dalam tampilan saat ini. Otorisasi diperiksa saat pengeditan atau penghapusan diterima, dan kebijakan produk menentukan batas waktu serta apakah penghapusan akan menghilangkan konten sepenuhnya, menandai sebagai tombstone, atau memicu penghapusan asinkron dari penyimpanan sekunder.
Pertanyaan Lanjutan 5: Bagaimana jika anggota yang telah dihapus terhubung kembali dengan kursor lama?
Layanan sinkronisasi mengevaluasi interval keanggotaan pengguna, bukan sekadar memeriksa kepemilikan kursor. Layanan hanya mengembalikan event yang diizinkan untuk pengguna tersebut dan menyertakan batas waktu penghapusan keanggotaan. Kursor hanyalah penunjuk posisi yang bersifat opak, bukan token otorisasi. Lampiran yang di-cache dan salinan lokal memerlukan mekanisme pencabutan dan retensi yang terpisah karena server tidak dapat menghapus data yang sudah tersimpan di perangkat secara fisik.
Pertanyaan Lanjutan 6: Bagaimana Anda mendukung saluran publik yang sangat besar?
Desain fan-out-on-write untuk grup berisi 200 anggota tidak lagi cocok. Simpan log saluran sekali saja, biarkan pengikut mengambil data (pull) berdasarkan kursor, lakukan fan-out hanya untuk metadata ringkas belum dibaca atau notifikasi push, gunakan caching untuk segmen populer, dan hilangkan tanda terima pengiriman individual. Partisi, moderasi, penemuan konten, dan beban ekstrem pada saluran populer menjadi masalah utama yang harus ditangani, sehingga skenario ini harus diperlakukan sebagai jenis beban kerja yang berbeda, bukan sekadar meningkatkan parameter angka pada desain yang sama.