Mengapa GSI DynamoDB eventually consistent
Anda menulis item, segera Query Global Secondary Index untuk item itu, dan
mendapat tidak ada yang kembali — meskipun write berhasil dan GetItem
tabel dasar mengembalikan item dengan baik.
Tidak ada yang rusak. Anda telah menabrak properti GSI yang paling mengejutkan: setiap baca GSI adalah eventually consistent. Ada jendela singkat setelah write di mana indeks belum menyusul.
Apakah GSI DynamoDB eventually consistent?
Ya — setiap baca Global Secondary Index adalah eventually consistent, tanpa cara
opt-out. Write Anda commit ke tabel dasar dulu, lalu menyebar secara asinkron ke
indeks, jadi yang dikeluarkan tepat setelah write bisa
mengembalikan baris basi atau hilang. DynamoDB tidak menawarkan flag
ConsistentRead untuk GSI.
- GSI adalah tabel terpisah yang direplikasi secara asinkron — write Anda commit ke tabel dasar dulu, lalu menyebar ke indeks.
- Tidak ada flag
ConsistentReaduntuk GSI. Tidak seperti tabel dasar, Anda tidak bisa memaksa strong read untuk menutup celah. - Read-your-own-writes dari tabel dasar, bukan GSI. Anda sudah memegang tepat setelah write.
- Tegakkan uniqueness dengan conditional write, bukan query GSI. Celah propagasi mengubah cek "apakah ini diambil?" menjadi race.
Gejalanya: sign-up yang "tidak bisa menemukan dirinya"
Ambil tabel Members untuk layanan akun pengguna. Tabel dasar di-key by id
internal, tetapi pengguna login by email, jadi ada GSI lookup email:
| PK | SK | displayName | |
|---|---|---|---|
| ACC#a1f9c | PROFILE | ada@northwind.test | Ada L. |
| GSI1PK | GSI1SK |
|---|---|
| ada@northwind.test | ACC#a1f9c |
Alur sign-up melakukan dua hal beruntun: PutItem member baru, lalu
Query EmailIndex WHERE GSI1PK = "ada@northwind.test" untuk memastikan tidak
ada orang lain mengklaim alamat itu dan untuk memuat profil.
Jalankan dua panggilan itu beberapa milidetik terpisah dan Query bisa
mengembalikan nol item. Lakukan lagi sedetik kemudian dan barisnya ada.
Write tidak gagal — indeks hanya belum di-update.
Mengapa ini terjadi: GSI direplikasi secara asinkron
GSI adalah tabel terpisah yang dikelola secara internal dengan partisi dan skema key sendiri. Ia tidak dipelihara di dalam transaksi yang sama dengan write tabel dasar Anda.
Saat Anda PutItem, DynamoDB commit secara durable ke tabel dasar, mengakui
write Anda, dan lalu menyebarkan perubahan ke tiap GSI secara asinkron.
Dokumentasi GSI
AWS menyatakannya dengan jelas: GSI hanya mendukung baca eventually consistent.
Delay propagasi antara write tabel dasar dan update indeks biasanya sepecahan detik — tetapi tidak dijamin dan tidak dibatasi di bawah load. Mendesain seolah-olah dibatasi adalah jebakannya.
Delay itu adalah trade-off desain Dynamo asli. Paper Amazon Dynamo 2007 memilih availability dan partition tolerance di atas strong consistency.
GSI mewarisi garis keturunan itu. Loose coupling yang memungkinkan indeks berskala dan tetap writable independen dari tabel dasar.
Celah antara 200 OK dan "replikasi perubahan" adalah jendela di mana baca
indeks Anda basi. Tidak ada flag consistent-read yang menutupnya.
Tidak seperti tabel dasar — di mana Anda melewatkan ConsistentRead = true
untuk memaksa GetItem/Query —
GSI menolak opsi itu secara flat.
LSI bisa dibaca strongly karena berbagi partisi tabel dasar; lihat GSI vs LSI mengapa pembedaan itu ada.
Biaya baca pada GSI
Query GSI menagih RCU on-demand indeks di us-east-1 seperti baca lain —
0,5 RCU per blok 4 KB eventually-consistent — dan Anda tidak bisa membayar
ganda untuk strong consistency karena ConsistentRead ditolak. Lag propagasi
gratis; baca indeks tidak. Bandingkan tarif baca tabel dasar vs GSI di
kalkulator harga.
Jebakan lebih halus: nilai lama yang basi, bukan hanya yang baru hilang
Kasus baris-hilang adalah yang jelas. Bug yang lebih sunyi adalah membaca nilai sebelumnya yang basi.
Katakan Ada mengubah email dari ada@northwind.test ke ada.l@northwind.test.
Tabel dasar meng-update secara atomik, tetapi sejenak GSI masih bisa
mengembalikan entri indeks lama.
Lookup terhadap nilai baru miss, sementara nilai yang ditinggalkan masih resolve.
Lebih buruk: jika Anda Query GSI dan menulis kembali berdasarkan apa yang Anda baca, Anda bisa bertindak pada nilai yang tidak lagi ada. Perlakukan setiap baca GSI sebagai snapshot yang mungkin tertinggal dari realitas.
Desain mengitarinya — jangan lawan
Jendela propagasi nyata, jadi perbaikannya arsitektural, bukan knob retry yang Anda toggle. Empat pattern, kira-kira dalam urutan preferensi:
Baca write Anda sendiri dari tabel dasar. Tepat setelah write Anda sudah memegang primary key (
ACC#a1f9c), jadi lakukanGetItemstrongly consistent pada tabel dasar alih-alih Query GSI.GSI untuk access pattern lain — "saya punya email, temukan akun" — bukan untuk mengonfirmasi write yang baru Anda buat.
Tegakkan uniqueness dengan guard item, bukan GSI. Jangan percaya query GSI untuk membuktikan email belum diklaim — celah propagasi menjadikannya race yang dua sign-up simultan bisa kalah keduanya.
Sebagai gantinya, tulis item uniqueness khusus yang di-key pada email itu sendiri (
PK = "EMAIL#ada@northwind.test") di dalamTransactWriteItemsdenganConditionExpressionattribute_not_exists(PK).Kondisi tabel dasar strongly consistent, diterapkan secara atomik, yang benar-benar menegakkan uniqueness.
TransactWriteItems: - Put member item (PK = ACC#a1f9c, SK = PROFILE) - Put uniqueness item (PK = EMAIL#ada@northwind.test) ConditionExpression: attribute_not_exists(PK)Jika sign-up kedua race untuk alamat yang sama, kondisinya gagal dan seluruh ditolak — tanpa GSI, tanpa delay propagasi, tanpa double-claim.
Bangun dan preview kondisi
attribute_not_existsitu dengan Pembuat Ekspresi DynamoDB sebelum Anda wire ke kode.Toleransi lag di UX. Saat baca GSI benar-benar alat yang tepat (login by email untuk pengguna yang sudah ada), jendelanya sub-detik dan tidak berbahaya — akun yang established sudah lama dipropagasi.
Cadangkan jalur tabel dasar strongly consistent hanya untuk momen read-after-write.
Re-query, jangan asumsikan. Jika workflow harus mengamati item brand-new lewat GSI, perlakukan hasil kosong sebagai "belum terlihat," bukan "tidak ada," dan re-query setelah backoff singkat.
Tetapi prefer pattern 1 dan 2, yang menghilangkan tebakan sepenuhnya.
Lihat celah propagasi sendiri
Cara tercepat membangun intuisi adalah menyaksikannya terjadi. Di DynoTable Anda put item ke tabel dasar dan segera Query GSI di tab kedua.
Pada tabel yang loaded Anda sesekali menangkap indeks tertinggal dari data dasar, lalu menyaksikannya converge pada refresh berikutnya.
Melihat lag dengan data Anda sendiri membuat aturan "baca write Anda sendiri dari tabel dasar" jauh lebih melekat daripada diagram mana pun.
Jebakan dan langkah selanjutnya
- Jangan gate logic pada GSI read-after-write. Cek uniqueness, konfirmasi "apakah write saya mendarat", dan loop read-modify-write milik tabel dasar strongly consistent.
- Jangan jangkau
ConsistentReadpada GSI — tidak diizinkan dan akan error. - Jangan model access pattern sebagai GSI saat key dasar sudah menjawabnya. Layani baca dari primary key dan Anda melewatkan jendela propagasi sepenuhnya.
Memilih bentuk key yang tepat adalah seluruh permainan di
single-table design; mengetahui kapan
Query mengalahkan Scan menjaga Anda dari indeks sejak awal
(Query vs Scan).
Bangun dan uji ConditionExpression uniqueness Anda di
DynamoDB Expression Builder. Lalu
coba DynoTable untuk menyaksikan write tabel dasar menyebar ke GSI
secara real time, dan desain key Anda agar jendela eventual-consistency tidak
pernah menggigit.