So I've been reading this article on the wiki about saving player data, but something seemed a bit weird to me. (Here's the link: http://wiki.roblox.com/index.php?title=Saving_Player_Data)
In their example, they demonstrated making a save data function using only SetAsync
, but in their article explicitly about the data store service
(along with it's API), they say how using only SetAsync
to save existing data can be dangerous, since GetAsync
can return cached data.
Here's the function they gave in their article that is somewhat controversial to me:
local function savePlayerData(player) playerData:SetAsync(player.UserId, sessionData[player]) -- SetAsync to save EXISTING data? end -- And now we're going to use it in a loop? local function autosave() while wait(AUTOSAVE_INTERVAL) do for player, data in pairs(sessionData) do savePlayerData(player) end end end
Here's the quote from the original wiki article documenting the data store's SetAsync
function:
"Sets the value of the key. This overwrites any existing data stored in the key. If the previous value of the key is important, use UpdateAsync. Using GetAsync to retrieve a value and then setting the key with SetAsync is dangerous because GetAsync sometimes returns cached data, and other game servers may have modified the key"
(Initial data store article here: http://wiki.roblox.com/index.php?title=Data_store)
So... Are there some exceptions to when you can use SetAsync to save existing data, does it not matter, or did they just make a mistake?
If anyone could clear this up for me, I'd really appreciate it. Thanks.
When it comes down to it, this is about race conditions.
Because the game server and datastore server are in physically different machines, it can take quite a bit of time for reads and writes to happen.
If you did something like
a = Data:Read() print(a) --> 10 Data:Write(11) b = Data:Read() print(b) --> 10
You'll probably not get the 11
out. This is further complicated by having many different servers all talking to the data store at the same time. You have to make sure your code will never "compete" with other reads/writes.
:UpdateAsync
let's you avoid this trouble most of the time. It gives you the current value, and guarantees no one else will write before you do.
Only if the current value does not depend at all on the previous value (not even on a variable that was loaded from that in the data store) is it for sure safe to use :SetAsync
, or if you are sure that no other writes will happen at the same time.
Examples of this might be
Write the current time to an associated user id when they join
Updating only when the player leaves, or only once in a while, for a particular player * player is only in one server at a time * writes have almost no probability of conflicting since they are far apart * close together writes are unlikely to disagree
If you ever have something like Set( Get() + 1 )
, you should not be using SetAsync
and should be using UpdateAsync