Pagination membagi hasil besar menjadi beberapa bagian, tetapi pilihan mekanismenya memengaruhi konsistensi integrasi. Offset mudah dipahami sebagai halaman, sedangkan cursor membawa posisi dari hasil sebelumnya. Saat data berubah selama pembacaan, keduanya dapat menunjukkan perilaku yang sangat berbeda.
Bagaimana offset dan cursor bekerja?
Offset meminta melewati sejumlah record lalu mengambil limit berikutnya. Request mudah dibuat dan pengguna dapat melompat ke halaman tertentu. Cursor membawa token atau keyset yang mewakili posisi terakhir, lalu server melanjutkan setelah posisi itu. Cursor tidak harus mengungkap isi key dan sebaiknya diperlakukan opaque oleh client.
Keduanya membutuhkan order yang deterministik. Mengurutkan hanya berdasarkan timestamp yang dapat sama menghasilkan batas ambigu; tambahkan tiebreaker unik. Kontrak field dan urutan perlu ditulis sebagaimana dibahas pada kontrak data.
Apa yang terjadi ketika data berubah?
Pada offset, insert di bagian awal dapat menggeser record sehingga satu record terlihat dua kali atau terlewat. Delete juga mengubah posisi. Cursor yang memakai keyset stabil melanjutkan setelah nilai terakhir, sehingga pergeseran sebelumnya tidak memengaruhi posisi. Namun update pada kolom urut dapat tetap memindahkan record melewati boundary.
Jika bisnis memerlukan satu pandangan konsisten, API harus menyediakan snapshot, watermark, atau mekanisme lain. Cursor saja tidak membekukan dataset.
Bagaimana performa dan UX dibandingkan?
| Aspek | Offset | Cursor |
|---|---|---|
| Lompat halaman | Mudah | Biasanya berurutan |
| Data terus berubah | Rawan pergeseran | Lebih stabil dengan keyset |
| Query offset besar | Dapat makin mahal | Dapat memakai indeks posisi |
| Debug manual | Nomor sederhana | Token opaque |
| Pemulihan | Simpan offset | Simpan cursor aman terakhir |
Apa yang harus dijelaskan oleh kontrak cursor?
Jelaskan masa berlaku, hubungan dengan filter dan sort, perilaku jika cursor tidak valid, batas ukuran halaman, serta apakah cursor dapat dipakai ulang. Client tidak boleh memodifikasi token opaque. Jangan memasukkan rahasia atau data pribadi ke cursor yang hanya di-encode; encoding bukan enkripsi.
Untuk proses panjang, simpan checkpoint hanya setelah halaman selesai diproses. Bila halaman gagal di tengah, tentukan apakah seluruh halaman diulang dan apakah tujuan idempoten. Hubungkan dengan batas retry.
Bagaimana filter dan watermark membantu?
Polling perubahan sering memakai updated_at lebih besar dari watermark. Gunakan kombinasi timestamp dan ID unik agar nilai waktu sama tidak terlewat. Overlap kecil dapat melindungi keterlambatan commit, tetapi membutuhkan deduplikasi. Simpan watermark dari data sumber, bukan jam consumer yang mungkin berbeda.
Jika record dapat diperbarui ke timestamp lama atau dihapus tanpa tombstone, filter tersebut tidak cukup. Tambahkan mekanisme perubahan resmi atau rekonsiliasi. Baca webhook versus polling untuk melihat konteks discovery perubahan.
Skenario apa yang harus diuji?
- Insert sebelum dan sesudah posisi saat paging berlangsung.
- Delete record pada halaman yang telah dibaca.
- Dua record memiliki nilai sort sama.
- Cursor kedaluwarsa, rusak, atau dipakai dengan filter berbeda.
- Proses restart setelah satu halaman berhasil.
- Halaman kosong sebelum benar-benar selesai.
NitroNet atau platform lain dapat mengonsumsi API yang dipaginasi, tetapi artikel ini tidak mengklaim perilaku produk tertentu. Gunakan sample besar yang aman serta rekonsiliasi untuk memastikan jumlah akhir, bukan hanya keberhasilan setiap request.
Buat sedikitnya sepuluh ribu record dengan timestamp berulang dan ID unik. Selama client membaca halaman, jalankan insert, update, serta delete pada posisi berbeda. Bandingkan record akhir dengan snapshot referensi agar duplikat dan omission terlihat.
- Gunakan sort tanpa tiebreaker lebih dahulu untuk menunjukkan mengapa batas antarhalaman menjadi ambigu.
- Tambahkan ID sebagai tiebreaker lalu ulangi perubahan yang sama dan bandingkan hasil.
- Sisipkan record sebelum offset aktif untuk melihat pergeseran dan pembacaan ganda.
- Ubah nilai sort record yang belum dibaca sehingga berpindah melewati cursor.
- Coba cursor lama dengan filter baru dan pastikan server menolak atau mendefinisikan perilakunya.
- Restart consumer setelah checkpoint tersimpan, kemudian buktikan halaman sukses tidak hilang.
- Batasi ukuran halaman ekstrem agar satu request tidak menghabiskan memory atau waktu tujuan.
- Hitung ID unik, total record, dan checksum bisnis setelah traversal selesai.
Tidak ada mekanisme pagination yang menghilangkan kebutuhan kontrak dan rekonsiliasi. Offset dapat tepat untuk UI dan dataset stabil; cursor membantu traversal berurutan tetapi harus memiliki semantik yang jelas. Bila konsistensi snapshot diperlukan, sediakan mekanisme khusus dan jangan mengasumsikan cursor membekukan data.
Validasi dengan dataset yang berubah selama pembacaan. Tambahkan record sebelum posisi aktif, ubah nilai pengurutan, hapus record, lalu lanjutkan halaman berikutnya. Catat apakah hasil menjadi duplikat, hilang, atau tetap lengkap. Cursor perlu menjelaskan masa berlaku, scope filter, serta perilaku bila token tidak valid; offset perlu urutan deterministik dengan tie-breaker unik. Consumer juga harus memiliki batas halaman, deteksi loop, dan rekonsiliasi jumlah agar kesalahan server tidak menghasilkan proses tanpa akhir atau laporan yang tampak selesai padahal tidak lengkap.