Firebase allows for a resource to be updated transactionally. As I understand it, the client does this buy sending requests to the server saying "If the old value is X, make the new value Y". If there is contention the server could reject multiple updates form the client until one is accepted.
Now, what if I want to update multiple resources atomically?
What happens if the first update is accepted, and then the client is disconnected prior to the second update being accepted. Is there any way to enclose multiple updates in an atomic transaction? If not, is there an idiomatic solution to this problem?
UPDATE
It's now possible to update multiple locations atomically. See this blog post for details.
var mergedUpdate = {};
mergedUpdate[ 'users/' + userId + '/widgets/' + widgetId ] = true;
mergedUpdate[ 'widgets/' + widgetId ] = widgetData;
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
ref.update(mergedUpdate);
This doesn't enforce transactional data (if value is currently X, make it Y), but this part can be moved to security rules. For example, if we want to update two counters at the same time, we could add rules as follows:
{
"counter1": {
".validate": "newData.val() === (data.val()||0)+1"
},
"counter2"1 {
".validate": "newData.val() === (data.val()||0)+1"
}
}
Now we can attempt the same multi-path update as above. If the values have changed since we last read them from the server, the attempt will fail. We can check if( error.code === 'PERMISSION_DENIED' ) { ... }
to see if the failure was due to validation, and retry accordingly.
ORIGINAL POST
The only way to do this is to run a transaction on a common ancestor.
For instance, if you want to update /a/b/c and /a/x/y, you could run a transaction at /a and change both of the values.
The downside to this approach is that it can be expensive with network I/O, as all of the data inside the transaction needs to be downloaded and then sent back to the server.
A more complicated but potentially more powerful approach you might want to consider is restructuring your data so that rather than storing the actual values, you store a history of the edits. For example, if you're storing bank balance information, you could store a history of deposits and withdrawals. Then when you wanted to get the balance, you would play back the whole history and calculate the end balance.
The beauty of this approach is it lets you do atomic updates. For example, if you were transferring money from accountA to accountB, you'd just append an element to the end of the log saying "transfer from account A to accountB N dollars". Appending that single element is an atomic operation.
This is the approach we take with Firepad, our collaborative text editor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With