Consider the use case where I am embedding Address
document IDs into a User
document. Threads 1
and 2
run at the same time followed by the Check results
thread.
User:
addresses: [1, 2, 3]
Async Thread 1:
user = userCollection.find(userId)
user.addresses.push(4)
userCollection.update(user)
Async Thread 2:
user = userCollection.find(userId)
user.addresses.push(5)
userCollection.update(user)
Check results:
user = userCollection.find(userId)
user.addresses.includes(4) // result is non-deterministic
user.addresses.includes(5) // result is non-deterministic
Do I need to implement my own document level locks at the application level to prevent multi-threaded applications from overwriting data that other threads are currently writing?
Maybe I'm missing a built-in atomic function to append to an array? But what about the case of find / replace? I don't just want to 'push' a new value into the array but find an old ID that's been deleted and then remove it. And at the same time another thread wants to add to the array. I'm honestly not sure what the simplest solution is. I've written the problem in psuedo-javascript however I'm using golang for the project.
CodePudding user response:
According to official doc,
In MongoDB, a write operation is atomic on the level of a single document, even if the operation modifies multiple embedded documents within a single document.
You won't need to care a lot about atomicity for a single document. For your given example, you can simply do an update with an aggregate pipeline, which is available for MongoDB v4.2 .
db.collection.update({
"userId": 1
},
[
{
"$set": {
"addresses": {
"$setUnion": [
{
"$filter": {
"input": "$addresses",
"as": "a",
"cond": {
$ne: [
"$$a",
3 // element you want to remove
]
}
}
},
// element you want to add
[
4
]
]
}
}
}
])
Here is the Mongo playground for your reference.
If you need to deal with multi-document atomicity, you can opt for transactions, which is available for MongoDB v4.0