Sometimes between requests I’m getting the database out of sync. Well, truth be told, I think the write operation is ignored
nk.storage_write({ collection ="savegame", key = "savegame", user_id = player_id, value={..."some_value":2...} })
-- on the next request
local savegame_entry = nk.storage_read({ collection = "savegame", key = "savegame", user_id = player_id })
local savegame = object_id[1].value
print( savegame.some_value ) -- prints the old value
Then when I look the entry in the dashboard it has the old value indeed.
What really confuses me even more is if I execute this code on the first request…
nk.storage_write({ collection ="savegame", key = "savegame", user_id = player_id, value={..."some_value":2...} })
local savegame_entry = nk.storage_read({ collection = "savegame", key = "savegame", user_id = player_id })
local savegame = object_id[1].value
print( savegame.some_value ) -- prints the new value
But then again, the database has the old value.
On top of that, is not deterministic, sometimes I got the old value, but other time the new one. I guess this is due to the cache system
Is there anyway to check if the write operation has been successful? Could it be that it is just written on the cache?
EDIT:
Doing some little investigations I have two rpc request very close to each other and it seems like that when one is performed correctly the other one doesn’t.
Nakama does not add a cache layer in front of the database for storage objects.
If you’re seeing stale data in the console it might be because you looked up the value before changing it. The console view reads the data when you search for a value, not when you click it in the list. Make sure you refresh the view or search again to get the most recent data.
Otherwise it depends on which order you perform operations in. If new queries come in before or after the value is written they may see different data.
I’ll use some code as example for explaining what’s happening
Here’s the client code (Unity):
AddGold( 50 ); // these two functions call an async-await call inside
AddGold( 50 );
And this is the server code:
local nk = require("nakama")
nk.register_rpc(addGold, "addGold")
local function addGold(context, payload)
local json = nk.json_decode(payload)
local gold = json.qty
local object_id = {{ collection = "savegame", key = "inventory", user_id = context.user_id }}
local inventory = nk.storage_read({object_id})[1].value
inventory.gold = inventory.gold + gold
nk.storage_write(object_id)
end
The amount of gold in the database after executing the client code is 50, one of the request is lost.
I’m running nakama server locally, I don’t know if it has something to do
If the AddGold functions execute concurrently then what’s likely happening is both read the original record (which I assume has a 0 value currently written), both add 50 to it (each going from 0 to 50), and both write it to database. It’s a typical concurrent access problem.
What you’ll want to do is use the version parameter to ensure you can sequence the writes:
local nk = require("nakama")
local function addGold(context, payload)
local json = nk.json_decode(payload)
local gold = json.qty
local object_id = {{ collection = "savegame", key = "inventory", user_id = context.user_id }}
local existing_object = nk.storage_read({object_id})[1]
local new_object = {
collection = existing_object.collection,
key = existing_object.key,
user_id = existing_object.key,
value = existing_object.value,
version = existing_object.version
}
new_object.value.gold = existing_object.value + gold
local status, err = pcall(nk.storage_write, {new_object})
if (not status) then
-- The write has been rejected, decide if you want to
-- retry or return an error to the client, who may then retry
-- or display a warning to the user etc
end
end
nk.register_rpc(addGold, "addGold")
The important part is the version field in the new object above. That instructs the server to only run that storage write operation if the version of the object in the database hasn’t changed between when you read it and when you try to re-write it.