Condition Expression DynamoDB: Panduan Lengkap (dengan Contoh)
Condition expression adalah predikat yang dievaluasi DynamoDB pada item yang ada sebelum ia mengomit write Anda. Jika predikatnya salah, penulisan ditolak dan
tak ada yang berubah. Ia adalah hal terdekat yang dimiliki DynamoDB dengan sebuah klausa
WHERE pada sebuah penulisan — dan satu-satunya cara aman untuk menegakkan sebuah invarian.
Bagaimana condition expression DynamoDB bekerja?
Sebuah condition expression adalah sebuah predikat yang dievaluasi DynamoDB di sisi server terhadap item saat ini sebelum mengomit sebuah penulisan. Jika ia benar, penulisan berlanjut; jika salah, penulisan ditolak dengan ConditionalCheckFailedException dan tak ada yang berubah. Ia melipat pemeriksaan dan mutasi ke dalam satu operasi atomik, sehingga pemanggil yang bersamaan tak bisa berlomba dengan sebuah pembacaan basi.
- Ia adalah penjaga, bukan filter.
ConditionExpressionberjalan di sisi server pada item saat ini; hasil salah menggagalkan penulisan denganConditionalCheckFailedException. - Ia menggantikan baca-lalu-tulis. Tanpa perjalanan bolak-balik
SELECTlaluUPDATE— pemeriksaan dan mutasi adalah satu operasi atomik, sehingga dua pemanggil tak bisa berlomba. - Ia gratis untuk menolak, tak gratis untuk berjalan. Sebuah penulisan bersyarat yang gagal tetap menghabiskan write capacity. Sebuah penulisan yang ditolak menagih WCU untuk ukuran item yang ada yang diperiksa terhadapnya (minimum 1) — sebuah create-if-absent yang gagal berbiaya 1 WCU.
Datang dari SQL, Anda akan membaca baris itu, memeriksanya di kode aplikasi, lalu memperbarui. Di DynamoDB, celah antara pembacaan dan penulisan itu adalah sebuah bug perusakan data yang menunggu seorang pemanggil yang bersamaan. Condition expression menutup celah itu.
Di mana ia berlaku
Anda melampirkan sebuah ConditionExpression ke PutItem, UpdateItem, DeleteItem, dan
setiap aksi di dalam TransactWriteItems. Ia bukan bagian dari Query atau Scan —
itu menggunakan FilterExpression, yang adalah hal berbeda pada jalur pembacaan.
Perbedaan itu menjegal orang, jadi jadilah tepat:
ConditionExpression | FilterExpression | |
|---|---|---|
| Jalur | Penulisan (Put/Update/Delete) | Pembacaan (Query/Scan) |
| Efek saat gagal | Menolak seluruh penulisan | Membuang item dari hasil |
| Melihat | Item saat ini, pra-penulisan | Setiap item kandidat, pasca-pembacaan |
| Biaya | Penulisan yang gagal tetap ditagih | Item yang difilter tetap ditagih untuk pembacaan |
Keduanya berjalan di sisi server. Perbedaannya adalah apa yang "salah" lakukan: sebuah kondisi membatalkan sebuah mutasi; sebuah filter hanya menyembunyikan sebuah baris yang sudah Anda bayar untuk dibaca. (AWS: Ekspresi kondisi)
Fungsi yang akan benar-benar Anda gunakan
Bahasa kondisinya kecil. Para pekerja kerasnya:
attribute_exists(path)/attribute_not_exists(path)— apakah ini ada pada item? Idiom klasik untuk "buat hanya jika absen" / "perbarui hanya jika ada".- Komparator —
=,<>,<,<=,>,>=— terhadap sebuah nilai atau atribut lain. attribute_type,begins_with,contains,size— pemeriksaan tipe dan string/set.BETWEEN … AND …,IN (…)— rentang dan keanggotaan.AND,OR,NOT, tanda kurung — untuk menggabungkan yang di atas.
attribute_not_exists pada adalah cara kanonis untuk membuat
PutItem berperilaku seperti sebuah insert yang tak akan menimpa sebuah item yang ada —
DynamoDB tak punya operasi "insert" terpisah, jadi kondisi adalah semantik insert-nya.
(AWS: Referensi operator perbandingan dan fungsi)
Sebuah contoh yang dikerjakan: menjaga sebuah buku besar dari overdraft
Ambil sebuah buku besar perbankan. Setiap akun adalah satu item:
PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents = 0Invariannya: sebuah debit tak boleh pernah mendorong saldo yang tersedia di bawah nol, dan Anda tak boleh pernah mendebit sebuah akun yang tak ada. Dua aturan, keduanya bisa ditegakkan di dalam penulisan itu sendiri.
Cara yang salah (jebakannya)
GetItem ACCT#a7f3 / BALANCE → clearedCents = 50000
if (50000 >= 30000) ... ← app-side check
UpdateItem SET clearedCents = 20000
Di antara GetItem dan UpdateItem, sebuah debit kedua bisa membaca 50000 yang sama,
lolos pemeriksaannya sendiri, dan menulis juga. Keduanya berhasil; akunnya menjadi negatif.
Ini adalah sebuah lomba baca-modifikasi-tulis, dan tak ada validasi sisi-aplikasi seberapa
pun yang memperbaikinya — pemeriksaan dan penulisan adalah operasi terpisah.
Cara yang benar
Lipat pemeriksaan ke dalam penulisan. Debit 30000 sen, bersyarat pada akun yang ada dan memegang cukup:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = clearedCents - :amt
ConditionExpression:
attribute_exists(PK) AND clearedCents >= :amtdengan :amt = 30000. Jika saldonya terlalu rendah, atau item-nya tak pernah dibuat,
DynamoDB menolak penulisan dengan
ConditionalCheckFailedException
dan saldonya tak tersentuh. Debit yang bersamaan entah melihat saldo asli dan diperiksa terhadapnya, atau
melihat yang sudah diperbarui — tak pernah sebuah pembacaan basi yang ia beraksi atasnya.
Anda bisa membangun dan menyalin ekspresi persisnya — nama, nilai, dan semuanya — dengan
DynamoDB expression builder alih-alih merakit tangan
peta ExpressionAttributeValues.
Coba di sini juga — builder ini di-preset ke sebuah PutItem yang dijaga
(attribute_not_exists) sehingga Anda bisa membaca ConditionExpression yang dihasilkan:
Memeriksa penjaga di DynoTable
Saat sebuah penulisan bersyarat gagal, Anda ingin melihat keadaan nyata item-nya, bukan
menebaknya. Tarik item akunnya dan baca clearedCents secara langsung.

Baca penolakannya, jangan mencoba ulang secara buta
ConditionalCheckFailedException bukanlah sebuah kesalahan sementara — mencoba ulang
penulisan yang sama tak mengubah apa pun. Ia berarti sebuah aturan bisnis terpicu: dana tak
cukup, create duplikat, versi basi. Munculkan ia sebagai sebuah hasil domain, bukan sebuah
gangguan infra.
Dua hal membuat kegagalan bisa di-debug:
ReturnValuesOnConditionCheckFailure: ALL_OLD— DynamoDB mengembalikan item saat ini bersama kegagalannya, sehingga Anda bisa menampilkan "saldo tadinya 20000, Anda meminta 30000" tanpa sebuah pembacaan kedua. (AWS: Bekerja dengan item)- Membedakan dua alasan kegagalan.
attribute_exists(PK) AND clearedCents >= :amtmeruntuhkan "tak ada akun" dan "tak ada dana" ke dalam satu eksepsi. Jika pemanggil perlu membedakan keduanya, pisahkan menjadi dua penulisan atau periksa item yang dikembalikan.
Optimistic locking adalah trik yang sama
Pola nomor-versi hanyalah sebuah condition expression yang mengenakan topi berbeda. Simpan
sebuah atribut version; setiap penulisan menegaskan versi yang Anda baca dan
menaikkannya:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = :new, version = :next
ConditionExpression: version = :seenJika seorang penulis lain bergerak lebih dulu, version = :seen salah, penulisan ditolak,
dan Anda membaca ulang serta mencoba lagi. Beginilah cara DynamoDB melakukan kontrol
konkurensi tanpa kunci — tegaskan apa yang Anda lihat, gagal jika ia bergerak. (AWS:
Optimistic Locking with Version Number)
Staging area DynoTable menjalankan pola ini untuk Anda — sebuah
suntingan bersamaan muncul sebagai konflik untuk diselesaikan, bukan penulisan
yang hilang.
Jebakan dan langkah selanjutnya
- Nama yang bertabrakan dengan kata cadangan.
status,size,name, dan ~570 lainnya adalah cadangan. Beri alias denganExpressionAttributeNames(#s = status) atau permintaannya ditolak dengan sebuah ValidationException ('Attribute name is a reserved keyword'). Pemeriksa reserved word menerima nama atribut Anda dan mengembalikan map alias siap tempel. - Sebuah kondisi tak bisa mereferensikan item lain. Ia hanya melihat item yang ditulis.
Invarian lintas-item membutuhkan
TransactWriteItemsdengan sebuahConditionExpressionper-aksi, atau sebuahConditionCheckterhadap sebuah item sentinel. - Penulisan yang gagal tetap berbiaya WCU. Sebuah penjaga yang menolak 90% dari waktunya tetap menagih untuk penolakan itu. Asuransi murah, tapi tak gratis.
Untuk memodelkan key yang dijalankan oleh penjaga ini, lihat single-table design dan Query vs Scan. Saat Anda siap mengeluarkan penulisan bersyarat terhadap data nyata, unduh DynoTable dan jalankan ia terhadap tabel Anda sendiri.


