Origin HTTP dapat menolak PUT atau DELETE sebelum menerapkannya ketika request membawa If-Match dan representasi yang dipilih tidak lagi memiliki entity tag yang diterima. Kondisi ini mengubah representation validator menjadi write precondition: client dapat menyatakan bahwa mutasi hanya valid terhadap state yang masih cocok dengan versi yang sebelumnya diamati.
Mekanisme ini menangani boundary konkurensi tertentu. Ia dapat mencegah satu client diam-diam mengganti resource state setelah client lain mengubah representasi yang dipilih. Namun ia tidak mengubah HTTP menjadi transaction protocol, tidak mengunci resource antar-request, dan tidak menjamin bahwa entity tag mewakili setiap bagian application state yang terlibat dalam mutasi.
Precondition dievaluasi di origin
Misalkan client mengambil suatu representasi dan menerima strong entity tag:
HTTP/1.1 200 OK
ETag: "rev-41"
Content-Type: application/json
{"status":"draft","owner":"sam"}Replacement berikutnya dapat membawa validator tersebut:
PUT /documents/17 HTTP/1.1
If-Match: "rev-41"
Content-Type: application/json
{"status":"approved","owner":"sam"}Untuk If-Match, origin server membandingkan entity tag yang dikirim dengan entity tag milik representasi yang saat ini dipilih memakai strong comparison function. Jika tidak ada yang cocok, kondisinya false dan method yang diminta tidak boleh dijalankan melalui jalur failed precondition normal. Response 412 Precondition Failed dapat mengekspos konflik tersebut kepada client.
Boundary pentingnya adalah evaluasi sebelum method diterapkan. Client tidak memperoleh ownership atas "rev-41" ketika menerima response sebelumnya. Actor lain tetap bebas mengubah resource. Request berikutnya hanya berhasil jika precondition masih berlaku saat origin mengevaluasinya.
Ini adalah optimistic concurrency pada interface HTTP: lanjut tanpa mempertahankan lock lintas request, lalu tolak mutasi yang basis observasinya sudah stale.
Strong comparison penting untuk mutation guard
HTTP membedakan strong dan weak entity tag. Weak validator dapat menandai semantic equivalence yang cukup untuk sebagian kasus cache validation walaupun representation data tidak stabil byte-for-byte. If-Match justru mewajibkan strong comparison karena salah satu tujuannya adalah mencegah method yang mengubah state diterapkan setelah representation data berubah.
Aturan ini menciptakan constraint praktis. Weak entity tag seperti:
ETag: W/"rev-41"tidak dapat memenuhi strong comparison terhadap tag lain. API yang ingin mengekspos If-Match sebagai mutation guard karena itu membutuhkan validator dengan semantik yang kompatibel dengan strong validation untuk representasi yang dipilih.
Validator juga harus memiliki hubungan yang benar dengan state yang dilindungi. Opaque tag sudah cukup pada tingkat protocol; client tidak perlu mendekodenya. Namun jika implementasi mengeluarkan strong entity tag yang sama untuk dua representation state yang representation data-nya berbeda, validator tersebut tidak lagi memenuhi requirement strong-validator yang membuat precondition bermakna.
Database revision number, content digest, atau internal version identifier dapat digunakan dalam implementasi entity tag, tetapi HTTP tidak menetapkan satu skema storage. Server tetap bertanggung jawab memberi nilai validator dengan semantik yang diperlukan.
Stale write menjadi branch yang eksplisit
Pertimbangkan dua client yang sama-sama mengambil versi "rev-41".
client A reads "rev-41"
client B reads "rev-41"
client A PUT If-Match: "rev-41"
origin stores new state and emits "rev-42"
client B PUT If-Match: "rev-41"
precondition no longer matchesTanpa write precondition, request client B dapat mengganti state yang ditulis client A jika semantik method resource mengizinkan replacement tersebut. Dengan If-Match, basis yang stale terlihat oleh origin sebelum mutasi kedua diterapkan.
Ini mengubah failure mode dari silent overwrite menjadi hasil protocol yang eksplisit. Namun mekanisme ini tidak menentukan conflict resolution. Setelah precondition gagal, aplikasi dapat mengambil current state, membatalkan mutasi, menampilkan conflict kepada manusia, atau menghitung request baru. Policy tersebut berada di atas HTTP precondition itu sendiri.
Perbedaan ini juga membuat retry lebih presisi. Mengulang conditional mutation stale yang sama tidak membuat kondisinya menjadi current. Strategi retry yang hanya mengirim ulang If-Match: "rev-41" setelah resource berubah ke "rev-42" mempertahankan conflict, bukan menyelesaikannya.
Entity tag melindungi selected representation, bukan transaction database arbitrer
Entity tag terkait dengan representasi dari target resource. Application write dapat memiliki cakupan yang lebih luas.
Request ke /documents/17, misalnya, dapat memperbarui row document, menambahkan audit record, mengubah quota counter, dan memublikasikan event. If-Match menetapkan kondisi mengenai selected representation; ia tidak mendefinisikan atomicity untuk seluruh efek internal tersebut. Aplikasi dan storage system tetap menentukan apakah operasinya transactional, partially applied, compensating, atau dikoordinasikan dengan cara lain.
Boundary ini semakin penting ketika representation state diturunkan dari beberapa record. Jika entity tag hanya berubah ketika satu primary row berubah, tetapi rendered representation juga bergantung pada related row, validator mungkin tidak melacak semua perubahan representation data. Sebaliknya, validator yang dihitung dari seluruh selected representation dapat berubah ketika komponen mana pun berubah, sehingga berpotensi menolak write yang sebenarnya hanya bergantung pada subset state.
HTTP tidak otomatis memilih salah satu desain. Validator design menentukan granularitas conflict signal.
Masalah serupa muncul pada content negotiation. Sebuah resource dapat memiliki beberapa representasi yang dipilih berdasarkan request metadata. Conditional semantics mengasumsikan pemetaan yang stabil dari request ke selected representation ketika validator digunakan dengan cara ini. Server yang memvariasikan representasi membutuhkan perilaku generation dan selection validator yang konsisten dengan model tersebut.
If-Match: * menyatakan keberadaan, bukan identitas versi
Bentuk wildcard memiliki semantik berbeda:
If-Match: *Bagi origin server, kondisi ini true ketika target resource memiliki setidaknya satu current representation. Ia tidak menyatakan bahwa resource masih berada pada versi tertentu.
Karena itu wildcard berguna untuk operasi yang mengharuskan resource sudah ada tetapi tidak perlu mengikat request pada entity tag yang diamati sebelumnya. Ia tidak dapat menggantikan validator spesifik ketika aturan yang dimaksud adalah “terapkan mutasi ini hanya jika representasinya masih sama dengan yang saya lihat.”
Pola wildcard kebalikannya muncul pada If-None-Match: *. Pada unsafe method seperti PUT, ia dapat menyatakan create-only condition: jalankan method hanya jika tidak ada current representation. Ketiga bentuk ini melindungi boundary yang berbeda.
If-Match: * require an existing representation
If-Match: "v7" require a matching current representation
If-None-Match: * require no current representationKondisi tersebut adalah predicate tingkat protocol. Kegunaannya bergantung pada kesesuaian semantik target resource dengan pengertian existence dan identity milik aplikasi.
Date validator memiliki boundary precision berbeda
If-Unmodified-Since juga dapat melindungi request yang mengubah state ketika entity tag tidak tersedia. Kondisinya didasarkan pada modification date dari selected representation. HTTP memberi If-Match precedence ketika kedua field ada, memperlakukan kondisi entity-tag sebagai pengganti yang lebih akurat.
Modification time membawa constraint yang dapat dihindari entity tag. Timestamp resolution bisa lebih kasar daripada laju perubahan state, semantik terkait clock berada di origin server, dan date menjelaskan waktu alih-alih opaque representation identity. Properti ini dapat membuat date validator kurang presisi untuk mendeteksi perubahan yang terjadi berdekatan.
Strong entity tag tidak membutuhkan interpretasi timestamp. Ia hanya perlu mengikuti semantik validator yang ditetapkan HTTP. Pemisahan ini memungkinkan origin memakai monotonic revision token atau skema opaque lain tanpa mengekspos internal clock sebagai concurrency token.
Conditional write tidak menciptakan cross-resource isolation
Pemeriksaan If-Match yang berhasil menyatakan hal yang sempit: pada saat evaluasi, selected representation memenuhi precondition yang diberikan dan origin melanjutkan sesuai request semantics. Ia tidak membentuk serializable isolation lintas beberapa resource.
Dua conditional request terhadap resource identifier berbeda dapat sama-sama lolos walaupun aplikasi menganggap efek gabungannya saling konflik. Menjaga invariant seperti “hanya satu active lease boleh ada di collection ini” membutuhkan coordination boundary yang merepresentasikan invariant tersebut. Per-resource entity tag saja tidak menciptakannya.
Bahkan pada satu resource, implementasi server tetap penting. Check dan protected state transition membutuhkan mekanisme concurrency internal yang mempertahankan makna precondition. Jika aplikasi membaca version, melepaskan semua koordinasi, lalu menulis kemudian tanpa memastikan version masih current, race di dalam origin dapat merusak intended guard walaupun header HTTP terlihat benar.
Implementasi umum memetakan precondition ke atomic storage predicate, transaction dengan locking atau isolation yang sesuai, atau mekanisme compare-and-set lain. HTTP mendefinisikan kondisi eksternal; origin harus menghubungkannya ke internal mutation boundary yang tidak dapat berubah di antara comparison dan write.
Preconditions mengekspos conflict tanpa menentukan merge semantics
If-Match berguna justru karena jaminannya terbatas. Ia membawa observed representation state kembali ke origin dan membuat method execution bergantung pada apakah state itu masih cocok. Ini cukup untuk mengubah satu kelas accidental overwrite menjadi conflict yang dapat dideteksi.
Semua hal setelah deteksi tetap menjadi application policy. Server dapat mengembalikan failed-precondition response, sedangkan client memutuskan apakah current state membuat perubahan yang diinginkan menjadi obsolete, compatible, atau membutuhkan human arbitration. Protocol tidak menyimpulkan field-level independence, tidak menggabungkan JSON object, tidak mempertahankan intent, dan tidak memberi peringkat competing write.
Pemisahan ini membuat mekanisme HTTP tetap composable. Entity tag mengidentifikasi representation state di bawah validator rules; If-Match mengubah validator itu menjadi precondition yang dievaluasi origin; application semantics menentukan arti conflict di luar boundary tersebut.