I am trying to use Spring Data MongoDB transactions to avoid write skew errors. This diagram explains the flow that happens in my example. I also posted the source code as a whole in this repository.
My service method that reads and updates the same document is @Transactional
:
fun nextMessage(id: Int, text: String) = transactionReadWrite(id, text)
?.let { messages.save(it) }
@Transactional
fun transactionReadWrite(id: Int, text: String) = threads.findById(id)
?.write(text)
?.let { (threadUpdate, previousLatest) ->
threads.save(threadUpdate)
log.info("Replacing \"${previousLatest.text}\" with \"${threadUpdate.latestMessage.text}\"")
previousLatest
}
But when I call nextMessage()
with 5 different inputs in parallel, I can see some of them reading the same values at the same time, and then successfully updating the document:
15:37:06.964 : Opened connection [connectionId{localValue:3, serverValue:1264}] to localhost:27017
15:37:07.308 : Replacing "0" with "3"
15:37:07.308 : Replacing "0" with "4"
15:37:07.310 : Replacing "0" with "1"
15:37:07.430 : Opened connection [connectionId{localValue:5, serverValue:1265}] to localhost:27017
15:37:07.430 : Opened connection [connectionId{localValue:6, serverValue:1266}] to localhost:27017
15:37:07.435 : Replacing "1" with "2"
15:37:07.440 : Replacing "1" with "5"
15:37:07.447 : Replacing "5" with "another one"
How do you avoid write skew errors with Spring Data MongoDB transactions?
CodePudding user response:
According to the diagram, B3.update should not succeed, you need to use optmistic locking in the update, like updateOne({_id:xx, text:"1"}, $set: { text: "3" })
that will return 0 modified
Then Bobby needs to find the current entity and attempt the update again.