Skip to content

Instantly share code, notes, and snippets.

@sentenza
Last active October 9, 2016 17:28
Show Gist options
  • Save sentenza/0b741e7f94a0140349738cd6d2dde1f3 to your computer and use it in GitHub Desktop.
Save sentenza/0b741e7f94a0140349738cd6d2dde1f3 to your computer and use it in GitHub Desktop.

Firebase transactions: the state of the art

Let's start sayng that Firebase «has always supported updating multiple children of a node atomically», as described in [1]. But, what happens when you have two sibling nodes to update and you want to be absolutely sure of the atomicity of the writing process.

The ACID problem stands in front of you, little padawan

A common problem is to be ACID in a World of Key-values NoSQL.

  • Transaction is either performed fully, or none at all (Atomicity)
  • While performing transaction A, transaction B can not read not-yet-committed writes of A. However, A can read it's own writes and got correct answers (Isolation)
  • Once transaction is committed, it's definitely there (Durability)
  • If transaction is aborted, none of it's writes will ever be visible. Before and after the transaction the datastore has to be in a consistent state (Consistency).

In a galaxy far far away

Think, for example, about a simple problem: Igor has to transfer to Marco 5 tanks of his own. It seems to be a really common situation, right? :)

Let's concentrate ouselves to a bunch of issues (aka what about our tests?):

  • Does Igor have got enough tanks? (Seems to be a legitimate question)
  • What if Igor has enough tanks on his facility but tries to double-spend them (i.e. he transfers the tanks to Simone in the very moment as he transfer them to Marco)
  • What if Marco's facility got sequestrated (or, he sells the facility at the very same moment when the transfer happens)
  • What if the client lose their internet connection in the middle of this whole operation?

And TA-DA here comes the transaction spawns again!

Rewind, Pack and Go

In order to focus on our environment (Firebase) we have to say that we have made a denormalization, almost certainly. So, now we have a flattened data storage and this inevitably led to an hardness on querying sibling node, until we continue using an old version of the library.

Firebase, as reported here, can manage the atomicity of updating two different locations at the same time. It is also possible to start a transaction with a Java doTransaction() and an equivalent JS transaction()

Resources

  1. https://firebase.google.com/docs/reference/android/com/google/firebase/database/Transaction
  2. http://stackoverflow.com/a/17438028/1977778
  3. https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html
  4. https://firebase.google.com/docs/reference/android/com/google/firebase/database/Transaction
  5. https://firebase.google.com/docs/database/server/save-data#section-transactions
var igorRef = new Firebase('https://myapp.firebaseio.com/igor');
var update = { name: 'Igor', tanks: 5 }; // They were 10 at the beginning of the transaction
igorRef.transaction(function(person) {
for (var prop in update) person[prop] = update[prop];
return person;
});
Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
// Generate a new push ID for the new post
Firebase newPostRef = ref.child("posts").push();
String newPostKey = newPostRef.getKey();
// Create the data we want to update
Map newPost = new HashMap();
newPost.put("title", "New Post");
newPost.put("content", "Here is my new post!");
Map updatedUserData = new HashMap();
updatedUserData.put("users/posts/" + newPostKey, true);
updatedUserData.put("posts/" + newPostKey, newPost);
// Do a deep-path update
ref.updateChildren(updatedUserData, new Firebase.CompletionListener() {
@Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Error updating data: " + firebaseError.getMessage());
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment