I’m trying to port my server authoritative 2 player turn based game from GameSparks to Nakama and I’m intending to store the game states in Nakama Storage using GoLang. It’s more of a round based game rather than turn based so there could be a situation where both players submit their turns for a particular round at exactly the same time. Do I need to lock the database somehow while the server reads the game state, updates it and writes it back to storage, causing the server side script processing the other players turn to stall and wait for the lock to be released before it does the same ? If so, how do I do that ? I’m not very experienced with databases so not sure what the correct procedure is to prevent these type of conflicts. Thanks !
Hey @Spuudy we use optimistic concurrency control rather than explicit locking for handling write conflicts.
This involves exchanging a version string with the database to detect conflicts and retry with the most recent version of the data from the request with the stale view of the data.
This version string is built into our storage API. You can read more about it here: Heroic Labs Documentation | Collections
Since your game is turn-based, conflicts won’t happen very often so this should fit your use case.
Isn’t the version string only available for client side reads and writes though ? The game state data in storage will always be updated server side for our multiplayer games so I’m wondering whether Player 1’s RPC call to submit their turn and Player 2’s RPC call to also submit their turn could happen at exactly the same time and cause a conflict in the game state data or whether the RPC calls from the client are queued and processed one after the other rather than concurrently to avoid this problem ? It’s probably very rare that Player 1 submits their turn and the server reads the game state from storage and Player 2 submits their turn while this is happening and it’s RPC also reads the now stale game state before Player 1’s RPC server code has finished writing the updated game state to storage. Should be quite rare but I feel like this situation could happen ?
Versions are also returned from the runtime framework. They are meant to handle this problem.
I haven’t been able to find any examples of using the version conditional writes so am I right in assuming the following is the correct way to go about this … read the data object from storage which contains a version key/value pair and after modifying the data, I increment the version value before trying to write it back to storage using that version number and if the write fails then assume the data has been updated by another RPC running concurrently, so read the data object again and repeat ? Maybe bail with an error code to the client after say 5 attempts ?
Pretty close – you actually don’t have to increment the version yourself. The server itself checks to see if it’s the latest version and handles the updating of the version on its own if you are doing a write.
I think how you handle failures may depend on your gameplay logic. But this brings me to a separate point – why not scope the storage of the users’ turns to their own user id? In other words, pass the userId
parameter to the storage writes. This would avoid any write conflicts.
You can read more about conditional writes here: