Optimistic locking

I wonder how one could implement optimistic locking with Fauna. Generally the idea is quite simple as we add a version number to a document and then compare the version number in the update coming from a client with the version number of the document they are attempting to update. As we’re using AWS Lambdas the way to do it is “as close to the database as possible”. So we’d need to conditionally update a document in the query itself.
Are there best practices for this with regards to Fauna? DynamoDB uses conditional writes for this. Does Fauna have something similar?

Ideally you don’t need your OCC scheme at all: if you can express your computation as a read-write transaction you’ll get occ locking for free. If you need to host the computation in lambda then sure: add a version to the doc and when you write it back conditionally update it with If.

As Ben has correctly answered, the If() function is your friend. I would argue that Fauna has more than something similar, FQL is much more powerful compared to what Dynamo offers for complex and/or conditional transactions. Take a look at the following tutorial, at the end of the tutorial, a ‘spaceship warp’ is performed if and only if the calculation of another UDF function (included in the transaction) is within specific limits.

Each FQL statement, regardless how complex is one transaction and indeed, Fauna does optimistic locking so you just need to write your conditional logic in your query and probably don’t have to worry about it :upside_down_face:. I tried to write out a simple explanation on how Fauna does this here: https://css-tricks.com/consistent-backends-and-ux:-how-do-new-algorithms-help/

1 Like

So to put it into code something like this is plausible (Given that version and updateData are coming from the host language):

    currentVersion: Select(["data", "version"], Get(ref)),    
    Eq(Var("currentVersion"), version),
      { data: Merge({version: Add(Var("currentVersion"), 1)}, updateData) }),
    Abort("Stale data update attemted")

Am I correct?

Yes, that should work. It’s strange though that you would enter the ‘version’ from the host language. This seems to indicate that you are executing two queries/transactions separately which could be necessary in case you do some heavy backend-side processing before you can write. If that’s not the case, there is a lot you can place in one transaction with FQL, I probably need more insights on what you are trying to achieve but I have a gutfeeling that it can be done in a simpler way and/or that you might be trying to apply a Dynamo pattern which is not necessary in Fauna.

Apologies for the lack of context.

I’m trying to solve a problem where multiple users can potentially overwrite the changes of each other by issuing an update. As I’m using Lambda to actually work with Fauna (even if I were to drop Lambda from the equation the problem still stands) I can’t make sure that a user can only update the most recent version of the document anywhere but the update transaction itself.

So the solution is to require users to pass the version number along with the update data. Then if say two users simultaneously send an update to the same document one update will be applied and the second will fail as the version will have been incremented.

If I understand correctly:

  • you have a document locally (e.g. in a client)
  • your user decides to update it
  • you want to verify that he doesn’t update something that has just been updated and throw an error if that’s the case.

In that case, your approach with If() and Abort() will work fine.
A few thoughts that might help later on:

  • You can just as well use the ‘ts’ to verify that you are working with the last version which is safer since it’s guaranteed to always be updated (e.g. you can’t get around it by forgetting to update it in a query).
  • The upcoming streaming features might help you minimize failed updates by notifying the user immediately when something has changed.
1 Like

I thought about ts as well, seems like a cleaner design. Thank you!