Data loss starting two days ago

No repo, dev relations looked and said that (In their experience) that it was the most likely the case.

same, no clue what to do at this point.

We’ve also been having unexplained data loss issues in our game. No errors and it’s not consistent at all. Looks like it might be a Roblox bug rather than our own system

1 Like

When I joined my game to test the issue people were having with their data not loading, my own data loaded in as inexistent and the server assumed I was a brand new player(how could it know the datastore request was wrong?) This is definitely something roblox has changed, because Ive never experienced this before.

1 Like

That’s unrelated to this issue – that would be because you are attempting to write to keys within the 6 second write cooldown. Review this section of this thread: https://devforum.roblox.com/t/details-on-datastoreservice-for-advanced-developers/175804#heading--req

EDIT: seems unrelated, but if you think it is not unrelated, please DM engineering with your datastore framework so they can confirm

Replcaing GetAsync with UpdateAsync didn’t help. In fact, I have a friend developer who doesn’t even use UpdateAsync, uses datastores in a similar fashion to me and experiences no data loss whatsoever…

I have a feeling UpdateAsync is broke…

I’m not dropping the chance that I’m just a really bad dev… I just don’t think I can fix a script that worked completely fine for almost 2 years.

I’m noticing a lot of developers getting spammed with these messages who don’t believe they should be :eyes:

1 Like

If you are experiencing the issue and think you are dealing correctly with the write cooldown and no chance of running out of budget, you should DM Seranok / RoughSphereBlox the datastore framework so they can confirm it’s not user error and investigate what could be causing it (if you are comfortable with sharing it with engineering).

A few days ago, a lot of reports came in about “data loss” in Strucid but after I did some investigation, it appears that data isn’t actually lost but rather just not loading. Thankfully, Strucid had a backup DataStore which I was able to use to restore players’ data after their normal saves were overwritten by the new saves.

I have a bit of a hard time believing this, as numerous games reports this issue as a spontaneous event, all of varying concurrent player count.

As far as the observations I can make from this thread, it seems more reasonable that the way Data Stores works was changed recently, causing this new critical edge-case.
And to that end, the holiday traffic should’ve went down by now - schools have started days ago, and kids are occupied with that by now, so it would make more sense for the issues to occur 2 weeks ago at least.

I think I read somewhere that when you use UpdateAsync, you have to actually change the value in some way, otherwise it will not break the cache.
Edit: found the source

I received a spike in data loss reports in my old game (Roblox High School 1) starting this month. In that game’s code, I used UpdateAsync to retrieve the player’s data, but I didn’t change the player’s data during that UpdateAsync call. And if there was an error when loading the player’s data, I tried reloading it again after 3 seconds - this lines up with the caching issue described earlier.

I have not received any data loss reports for Roblox High School 2. There, I also use UpdateAsync to load player data, but I always update a value in the table (LastSaveTime) to ensure that the cache is always broken.

5 Likes

To everyone having issues: in order to figure out what is going on, it would be beneficial if you direct messaged me with a link to your game if you are having DataStore issues. Please send along with it ANY repo steps, error/warning messages/codes, or time of occurrences.

ALSO: The information I provided to @Belzebass was a proposed solution I made after looking at their specific data code. I can not confirm this was ever the issue, nor that it is a solution. So far we have not been able to repo a cached nil being returned. If anyone can repo this please let us know.

5 Likes

Was there any change to the datastore coding? Because I still have players entire data getting wiped with me being unable to do anything about that. I’m having to manually change people’s datastores because people spent money on my game. That’s pretty critical.

Getting a LOT of reports about people losing all of their data in Island Royale

An idea that I’m trying out: Check if the player has a badge that you can’t get as a brand new player. For me it’s a level 5 badge. I check if they own the badge and if their data loads in as brand new(inexistant), then something is off and I return nil in UpdateAsync to reject it.

@LordJurrd :man_shrugging:

1 Like

Remember to check out RoughSphereBlox’s post when replying here; make sure all of the information and repro games go their way in DMs.

Figured I should post my new DataStore API. This prevents the current data loss issue (nil cache), but it does require you to initialize the values first to make them non-nil.

Variant result safeGetAsync(GlobalDataStore DataStore, string key, bool allowNil)
Variant value safeSetAsync(GlobalDataStore DataStore, string key, Variant value)
Variant value safeUpdateAsync(GlobalDataStore DataStore, string key, Variant value, Function transformFunction, bool allowNil)
Variant transformedValue transformFunction(Variant originalValue, Variant value)

function safeGetAsync(DataStore, key, allowNil, fails)
	local success, result = pcall(function()
		return DataStore:GetAsync(key)
	end)
	if not success or result == nil and not allowNil then
		warn(tostring(result) .. " Key =" .. key)
		local errorCode = tonumber(string.sub(tostring(result), 1, 3)) or 0
		if math.floor(errorCode/100) == 1 then
			return
		else
			wait((fails or 1)*6)
			return safeGetAsync(DataStore, key, allowNil, (fails or 1) + 1)
		end
	else
		return result
	end
end

function safeSetAsync(DataStore, key, value, fails)
	local success, result = pcall(function()
		return DataStore:SetAsync(key, value)
	end)
	if not success then
		warn(tostring(result) .. " Key =" .. key)
		local errorCode = tonumber(string.sub(tostring(result), 1, 3)) or 0
		if math.floor(errorCode/100) == 1 then
			return
		else
			wait((fails or 1)*6)
			return safeSetAsync(DataStore, key, value, (fails or 1) + 1)
		end
	else
		return value
	end
end

function safeUpdateAsync(DataStore, key, value, transformFunction, allowNil, fails)
	local success, result = pcall(function()
		return DataStore:UpdateAsync(key, function(originalValue)
			if originalValue == nil and not allowNil then
				return
			else
				return transformFunction(originalValue, value)
			end
		end)
	end)
	if not success or result == nil and not allowNil then
		warn(tostring(result) .. " Key =" .. key)
		local errorCode = tonumber(string.sub(tostring(result), 1, 3)) or 0
		if math.floor(errorCode/100) == 1 then
			return
		else
			wait((fails or 1)*6)
			return safeUpdateAsync(DataStore, key, value, transformFunction, allowNil, (fails or 1) + 1)
		end
	else
		return value
	end
end
1 Like

Hey everyone. I’ve put together a thread to request reliability improvements to DataStores. In my experience, DataStores frequently cause issues for players and developers and big data incidents are unfortunately too common. If you have experience with DataStore issues or reports of player data loss, please add on to the thread: Developers need reliable DataStores. What we have now is not acceptable

4 Likes

I tried doing some testing to figure out a repro. I tried using Network Simulator to force a DataStore error and then see if a cached nil value would be returned on the next request, but Network Simulator doesn’t seem to work as expected (it just makes the request infinitely yield, rather than error).

I did find a different issue though: If you call UpdateAsync, and an error occurs inside of the function you pass to UpdateAsync, the UpdateAsync call itself will not return an error; it will return a value of nil, and the rest of your code will carry on as if the data was successfully loaded.

Repro code (place this in a server script, follow the instructions, then run the game), this will throw an error on every other loading attempt:

--[[
	Before running the game, publish the game onto the website and run the following code in the command bar:
	game:GetService("DataStoreService"):GetDataStore("TestDataStore"):SetAsync("TestKey", 42)
	When the key is successfully accessed, you should always expect it to return the value 42.
--]]

local dataStore = game:GetService("DataStoreService"):GetDataStore("TestDataStore")

local loads = 0

while true do
	local result, reason, data
	
	repeat
		loads = loads + 1
		print("Trying to access datastore, result:")
		
		result, reason = pcall(function()
			data = dataStore:UpdateAsync("TestKey", function(val)
				if loads % 2 == 0 then
					error("Simulated loading error")
				end
				return val
			end)
		end)
		
		print("Was loading successful:", result)
		if reason then
			print("Reason:", reason)
		end
		
		wait(1)
	until result == true
	
	print("VALUE RETRIEVED FROM DATASTORE:", data)
	
	wait(7)
end

This may not be the issue that most people are facing, but if it’s possible for this method to encounter an error but still return a success + a nil value, it’s possible there might also be a different edge case that causes the same result.

@RoughSphereBlox

4 Likes