I’ve seen a lot of people ask about the whole concept of session locking and why UpdateAsync needs to be used to both set AND load a save. I feel like I could’ve done a better job explaining it in my video now that I understand it a bit better, but that ship has sailed. Maybe in the future. For those of you skeptical about the session locking guarantee that ProfileService offers, I’ll try to explain what happens atomically. Even the most experienced of programmers might learn something.
2 servers send an UpdateAsync request at the same time with the following code. This code loads a player’s saved data:
-- Initially data.SessionJobId will be nil (because no server has the player's data loaded)
print("Sending update async request")
local loadedData = PlayerStore:UpdateAsync(key, function(data)
if data == nil then
data = {}
end
print("Current SessionJobId: " .. tostring(data.SessionJobId))
-- Check if the save doesn't have a session lock OR if it's locked by this server. --
if data.SessionJobId == nil or data.SessionJobId == game.JobId then
data.SessionJobId = game.JobId
print("No session lock active, setting SessionJobId to " .. game.JobId)
-- Handle any fetching or writing right here --
return data
else
print("Session already locked")
return nil -- Returning nil in UpdateAsync only cancels the query. IT DOES NOT SET THE DATA TO NIL.
end
end))
print("Update async complete")
if loadedData ~= nil then
print("Loaded data!")
else
print("Could not load data due to active session lock")
end
It’s not possible to guarantee which server will be processed first as it’s up to Roblox’s internal network speed along with other small factors, but whichever server made the request that reaches Roblox’s datastore servers first we will label Server A. The other one is Server B.
Server A will have a JobId of efcc7b83-df93-4b44-abfd-b957d4182bba
.
Server B will have a JobId of 6fd555f0-cedd-47bd-916c-73677dc8c314
.
Server A’s server output will look like this:
--- SERVER A OUTPUT ---
Sending update async request
Current SessionJobId: nil
No session lock active, setting SessionJobId to efcc7b83-df93-4b44-abfd-b957d4182bba
Update async complete
Loaded data!
Server B’s server output will look like this, assuming that the 2 UpdateAsync requests were sent at the exact same time from different servers concurrently trying to modify the same key:
--- SERVER B OUTPUT ---
Sending update async request
Current SessionJobId: nil
No session lock active, setting SessionJobId to 6fd555f0-cedd-47bd-916c-73677dc8c314
Current SessionJobId: efcc7b83-df93-4b44-abfd-b957d4182bba
Session already locked
Update async complete
Could not load data due to active session lock
If you look at server B’s output, you can see that it appears the callback function ran twice (each time with different data in data
), despite us only using 1 UpdateAsync request. The reason why Server B’s updateasync callback ran twice is because that’s exactly how UpdateAsync works and why you should never use SetAsync to save player data.
It’s a very rare occurance, but Roblox’s databases will internally figure out which server should be processed first and if there’s any other requests made to that same key at the same time, they will all be rejected and the game servers that made them will have to re-process their callback functions. This makes it virtually impossible for 2 servers to have the same profile loaded at once.
Of course when releasing a profile, network errors happen, server crashes happen. This is why ProfileService might take up to 90 seconds to “steal” a profile (which is a lot better than an infinite amount of time!)