Bagaimana Kami Memangkas Latensi Redirect 10x dengan Meletakkan KV di Depan D1
20 April 2026
Di artikel sebelumnya kami memilih D1 daripada KV untuk Shrtnr. Model relasional menang karena kami membutuhkan analitik yang bisa di-query, bukan sekadar pencarian slug-ke-URL.
Alasan itu masih berlaku. Tetapi ternyata itu hanya setengah dari arsitektur yang akhirnya kami inginkan. Setiap redirect membayar round-trip dari edge ke primary D1 yang berada di satu wilayah saja. Untuk alat yang pekerjaan utamanya adalah menyingkir secepat mungkin, itu adalah anggaran latensi yang salah.
Jadi kami menambahkan KV sebagai write-through cache di depannya. D1 tetap sumber kebenaran; KV menangani jalur panas redirect. Ini adalah bagian yang seharusnya kami kirim dari hari pertama jika kami jujur tentang apa yang dibutuhkan redirect versus apa yang dibutuhkan bagian lain aplikasi.
Bottleneck: D1 adalah database single-region
Cloudflare Workers berjalan di 300+ lokasi edge. D1 tidak. Database D1 memiliki satu primary, berada di satu data center. Ketika Worker di Jakarta menangani sebuah redirect, query SQL menyeberangi jarak geografis apa pun yang memisahkan edge itu dari primary D1, dan round-trip terjadi sebelum 301 bisa dikirim.
Durasi SQL sendiri cepat: Cloudflare mendokumentasikan pembacaan terindeks di bawah satu milidetik. Tetapi durasi SQL bukan durasi request. Worker membuka koneksi, mengirim query, menunggu respons, dan mem-parse-nya. Latensi jaringan antara edge dan primary D1 mendominasi. Angka realistis: 30–200ms per redirect tergantung pada geografi.
Untuk pelacakan klik, kami sudah memiliki pola yang tepat. Penulisan klik berjalan di latar belakang via ctx.waitUntil() setelah redirect terkirim, sehingga pencatatan analitik tidak pernah memblokir pengunjung. Tapi resolusi slug, menjawab "ke mana tautan pendek ini sebenarnya mengarah?", terjadi di jalur request. Dan jalur request itu memiliki round-trip D1.
Solusi: KV sebagai cache edge untuk resolusi slug
Cloudflare KV adalah penyimpanan key-value yang meng-cache nilai di lokasi edge mana pun yang membacanya. Pembacaan pertama dari satu wilayah menarik dari penyimpanan pusat; pembacaan berikutnya dari wilayah yang sama dilayani dari cache lokal dengan latensi di bawah satu milidetik hingga beberapa milidetik. Tidak ada round-trip lintas wilayah.
Perubahannya: simpan pemetaan slug-ke-URL di KV. Gunakan KV sebagai pencarian pertama di setiap redirect. Jatuh kembali ke D1 hanya saat cache miss. Pertahankan D1 sebagai sumber kebenaran untuk semua yang lain: manajemen tautan, analitik klik, operasi admin.
Apa yang disimpan KV per slug
Setiap slug adalah kunci KV mentah, tanpa prefix, tanpa namespace. Nilainya adalah blob JSON kecil, sekitar 80–120 byte:
{
"url": "https://destination.com/page",
"disabled_at": null,
"expires_at": null
}Itu cukup untuk menyelesaikan redirect di edge. Satu pembacaan KV menggantikan satu query D1.
KV mendukung time-to-live pada kunci, yang menggoda untuk tautan kedaluwarsa. Kami tidak menggunakannya. Tautan bisa diaktifkan kembali. Seorang pengguna menyetel expires_at kembali ke null, dan slug perlu resolve lagi. Jika kunci KV sudah kedaluwarsa dan dihapus, mengaktifkannya kembali berarti merekonstruksi entri dari D1. Menyimpan timestamp di dalam nilai dan memeriksanya dalam kode membuat setiap transisi state tetap sederhana dan reversibel.
Dengan alasan yang sama, kami menyimpan slug yang dinonaktifkan di KV alih-alih menghapusnya. Jika slug yang dinonaktifkan hilang dari KV, handler redirect akan jatuh kembali ke D1 di setiap request. Bot yang menghantam slug yang dinonaktifkan akan menghasilkan trafik D1 tanpa batas. Menyimpan disabled_at di nilai KV memungkinkan handler mengembalikan 404 di edge tanpa menyentuh D1.
Jalur redirect: sebelum dan sesudah
Sebelumnya, setiap redirect adalah urutan yang sama:
Request tiba di edge
→ Query D1 (round-trip lintas wilayah)
→ Periksa disabled / expired
→ Redirect 301
→ waitUntil: catat klik di D1Setiap redirect membayar biaya latensi D1. Tidak ada lapisan cache.
Setelahnya, D1 hanya disentuh jika KV miss:
Request tiba di edge
→ Pembacaan KV (edge lokal, sub-milidetik untuk kunci yang di-cache)
→ [KV hit] Periksa disabled / expired → Redirect 301
→ [KV miss] Fallback D1 → populasi KV → Redirect 301
→ waitUntil: catat klik di D1Pada KV hit, D1 tidak pernah disentuh untuk redirect. Pencatatan klik tetap berjalan ke D1 via ctx.waitUntil() dan tetap tidak memblokir respons. Penulisan klik terjadi setelah pengunjung sudah dikirim ke tujuan.
Write-through: menjaga KV dan D1 konsisten
KV bersifat eventually consistent. Penulisan menyebar secara global dalam waktu sekitar 60 detik. Untuk URL shortener, itu adalah jendela yang dapat diterima. Itu besaran yang sama dengan propagasi DNS, dan lebih lemah dari keterlambatan yang kami toleransi untuk sebagian besar aksi admin. Tautan yang baru dibuat mungkin tidak redirect di setiap edge hingga satu menit. Tautan yang dinonaktifkan mungkin masih redirect hingga satu menit di beberapa lokasi. Keduanya bukan pelanggaran kebenaran; keduanya terbatas dan singkat.
Untuk membuat jendela itu sesempit yang KV sendiri izinkan, setiap mutasi menulis melalui secara sinkron. D1 ditulis terlebih dahulu. Itu adalah sumber kebenaran. Kemudian KV diperbarui sebelum respons API dikirim. Tidak ada waitUntil() pada penulisan cache. Jika put KV gagal, request gagal. Jika berhasil, jam konsistensi dimulai pada commit D1, bukan beberapa menit kemudian ketika pekerjaan async mengejar.
Setiap mutasi yang mengubah state slug (buat, perbarui, nonaktifkan, aktifkan, hapus) melalui satu service layer. Admin API, API publik, dan MCP tool yang dipanggil alur kerja AI kami semuanya melalui fungsi yang sama. Konsistensi cache ditangani di satu tempat, tidak tersebar di berbagai handler request.
Read-through: migrasi tanpa downtime
Kami tidak melakukan bulk-import slug yang sudah ada ke KV saat kami mengirimkan cache. Tidak ada skrip migrasi, tidak ada langkah deploy "pemanasan cache". Sebaliknya, handler redirect mempopulasi KV saat miss. Request pertama ke slug yang sudah ada membayar biaya D1 dan menulis entri ke KV. Setiap request setelah itu mengenai cache.
Secara operasional, ini adalah perubahan dengan risiko terendah yang bisa kami buat. Trafik produksi memanaskan cache saat tiba. Tidak ada momen di mana sistem berada di tengah migrasi dan melayani data basi atau hilang. Rollback trivial: hapus binding KV dan handler tetap menggunakan jalur fallback D1 yang selalu ada.
Dampak performa
Pada cache hit, langkah resolusi slug turun dari 30–200ms (round-trip D1) ke 1–5ms (pembacaan KV di edge lokal). Itu kira-kira pengurangan latensi 10x hingga 50x, tergantung seberapa jauh lokasi edge dari primary D1. Bagi pengunjung, itu adalah celah antara mengklik tautan dan melihat halaman tujuan mulai dimuat.
Volume query D1 turun bersamaan. TTL cache default KV adalah 60 detik. Untuk slug yang diakses lebih dari sekali per menit dari wilayah yang sama, hit rate mendekati 100%. Untuk slug yang diakses sekali dari banyak wilayah, kasus terburuk adalah satu query D1 per lokasi Cloudflare: sekitar 300 query untuk memanaskan segalanya, dibandingkan satu query per klik. Konservatif: 90–99% lebih sedikit query redirect terhadap D1.
Apa yang tetap di D1
KV mengambil alih satu pekerjaan spesifik: menjawab "ke mana slug ini menunjuk?" di edge. Semua hal lain tetap berjalan di D1.
Pencatatan klik tetap menulis ke D1 via ctx.waitUntil(). Setiap query analitik tetap berjalan sebagai agregasi D1: klik berdasarkan negara, referrer, perangkat, browser, OS, dan rentang waktu. Setiap operasi admin (membuat tautan, mengedit tujuan, mengelola API key, pencarian) tetap melalui D1. KV tidak bisa menjawab "tunjukkan sepuluh referrer teratas untuk tautan ini dalam 30 hari terakhir." Memang tidak pernah dimaksudkan begitu.
Kami menolak KV-saja, bukan KV-sebagai-cache
Jika Anda membaca artikel arsitektur kami sebelumnya dengan saksama, argumennya lebih sempit dari yang terdengar. KV "tidak memadai" ketika Anda butuh analitik, karena KV tidak bisa melakukan agregasi. Itu masih benar. Yang tidak kami katakan secara eksplisit, dan yang dibuat eksplisit oleh write-through cache, adalah bahwa "tidak bisa melakukan analitik" adalah pernyataan tentang KV sebagai database, bukan KV sebagai cache.
Untuk jalur panas redirect, KV adalah alat yang tepat. Ia direplikasi secara global, di-cache di edge, dioptimalkan untuk beban kerja read-heavy dengan penulisan yang jarang. Itu menggambarkan redirect URL hampir sempurna. Untuk analitik, D1 masih alat yang tepat: relasional, bisa di-query, bisa diindeks lintas dimensi. Menggunakan masing-masing untuk apa yang terbaik dilakukannya lebih baik daripada memilih satu dan berkompromi.
Kesimpulan artikel pertama tetap berdiri: "D1 adalah alat yang cocok" untuk produk yang membutuhkan pencarian cepat dan analitik yang bisa di-query. Yang berubah adalah kami menambahkan alat kedua untuk setengah pertama pekerjaan itu. Pencarian cepat tidak butuh SQL; mereka butuh cache yang hidup di edge.
Apa selanjutnya
Itu mencakup arsitektur seperti adanya sekarang. Artikel production-ops yang kami janjikan masih akan datang. Tiga sistem autentikasi dalam satu Worker, deployment satu klik, TypeScript SDK, dan berapa biaya menjalankan ini. Lapisan cache tidak mengubah semua itu; ia hanya membuat jalur redirect lebih tenang.
Tentang Oddbit
Kami adalah tim pengembang perangkat lunak senior yang berbasis di Bali, Indonesia, dengan akar di Swedia. Kami membangun perangkat lunak kustom untuk startup dan scale-up: aplikasi web, aplikasi mobile, platform SaaS, dan integrasi yang menghubungkan semuanya. Klien kami tersebar di Nordik, Eropa, dan Asia Tenggara.
Shrtnr adalah salah satu dari beberapa alat open-source yang telah kami bangun dan rilis. Lainnya termasuk Tanam (Firebase CMS dengan pembuatan konten AI), paket Flutter dengan sekitar seratus ribu unduhan di pub.dev, dan plugin Firebase Genkit untuk integrasi model AI.
Jika Anda sedang menyelesaikan keputusan arsitektur teknis, mengevaluasi edge computing untuk produk Anda, atau membangun sesuatu dari nol, hubungi kami. Kami lebih suka menunjukkan hasil kerja kami daripada menceritakannya.

