I am trying to further understand Fauna’s isolation levels and concurrency, but I am not sure I am thinking about it correctly.
Say I have an application that supports 2 concurrent users, both reading from and writing to the same document in a collection.
This simple starting document is enough to illustrate the example:
{
selections: []
}
Now let’s look some FQL that is responsible for handling user actions. In summary, this FQL does the following:
- Reads the document
- Performs some complex logic
- Creates a new version of the
selections
array with an item added - Updates the document
Query(
Lambda(["docId", "userId", "selection"],
Let({
docRef: Ref(Collection("MyDocuments", Var("docId")),
doc: Get(Var("docRef")),
selections: Select(["data", "selections"], Var("doc")),
// Complex logic
newSelections: Append(
{ userId: Var("userId"), selection: Var("selection") },
Var("selections")
),
},
Update(
Var("docRef"),
{
data: {
selections: Var("newSelections"),
}
}
)
)
Given that FQL, let’s dive into a scenario where the 2 users make selections simultaneously. Is this order of events possible?
- Assuming the code in the
Let
portion of the FQL can run concurrently, execution from both user 1 and user 2 concurrently read theselections
array in the document with this line:
selections: Select(["data", "selections"], Var("doc")),
Since the array was initialized as empty, both “threads” (for lack of a better term) will then append to the newSelections
array with their own selection.
So thread 1 from User 1 might store this in newSelections
:
[{ userId: 1, "User 1 selection" }]
and thread 2 from User 2 might store this in newSelections
:
[{ userId: 2, "User 2 selection" }]
This would result in data loss, where selections
only contains the result from either user. But I really want it to include both, and the order doesn’t actually matter as long as each entry is there.
So what I am wondering is if I need to push the Update
up further in the FQL, to essentially “lock” the document earlier in execution to prevent this concurrency issue.
Query(
Lambda(["docId", "userId", "selection"],
Let({
docRef: Ref(Collection("MyDocuments", Var("docId")),
Update(
Var("docRef"),
Let({
doc: Get(Var("docRef")),
selections: Select(["data", "selections"], Var("doc")),
// Complex logic
newSelections: Append(
{ userId: Var("userId"), selection: Var("selection") },
Var("selections")
)
},
{
data: {
selections: Var("newSelections"),
}
}
)
)
},
))
Logically, it would make sense that as soon as the first Update
begins, the document can no longer be written to until the first Update
ends, but I am not sure if this is how Fauna works.
So I guess my question is whether or not moving the Update
further up changes anything or if I’m thinking about concurrency and isolation levels all wrong.
Thank you!