REST users rejoice! Conditional requests are here!
How would you increment an integer in the Realtime Database? It's not always as easy as just grabbing a value, adding one, and writing it back again. When multiple users are simultaneously reading, updating and writing the same value, some users' writes could get "lost" due to race conditions.
Let's say you're using the Realtime Database to store upvotes on videos, and two of your users want to upvote the "Silly Cat Video" which currently stands at ten upvotes. Each user reads the value '10' from the database. They then independently increment the value locally and write '11' back to the database. "Silly Cat Video" now only has eleven upvotes when it should have twelve.
How do we prevent this? On a mobile or web client, you could use transactions, which have long been supported in our realtime SDKs. But what if you're using the REST API?
Good news for you: we've just added conditional requests to the REST API! While conditional requests work differently than the realtime transactions, we can still use them to safely perform atomic operations.
How to increment requests
Step 1: Request Data and ETag
# -i returns headers in the response, -H sets a header curl -i -H 'X-Firebase-ETag: true' 'https://<your-database>.firebaseio.com/videos/silly_cat_video/upvotes.json'
HTTP/1.1 200 OK ... ETag: ViJFJowpbyRvgGNPzPJdGeN+mCY= ... 10 // Value at the specified location
Step 2: Write the New Value
# -X sets the method (defaults to GET), -d provides data to the method curl -i -X PUT -d '11' -H 'if-match: ViJFJowpbyRvgGNPzPJdGeN+mCY=' 'https://<your-database>.firebaseio.com/videos/silly_cat_video/upvotes.json'
If no other users have incremented
upvotes.json the PUT request will succeed. If another user has, the request will fail and the response will contain the new ETag and data. Use these to calculate a new upvotes value and retry the PUT request.
But wait, what does all this
if-match stuff mean, and how do they work?
The Realtime Database's conditional REST requests use
ETags and the
if-match header from the HTTP conditional requests standard. An
ETag is a short unique identifier for data at a location: if the data changes, so does its
if-match, when set to an
ETag value, tells the database to only complete a request's operation if the data the request would overwrite matches the provided
Together, these two features enable a Compare and Swap (CAS) paradigm. Compare and Swap uses the two step process we saw in our video upvote example:
Fetch the location you wish to update along with its
Issue a PUT request with new data in the body and the
ETagof the old data in the
If the data at the location changes between Step 1 and Step 2, the database will not complete the operation and will return an error containing the new
ETagand data. You can use these values to construct a new PUT request and retry from Step 2.
For more details and to get started check out our REST conditional requests guide.