My game has data loss and I dont know how to fix it

So, my game has a lot of data loss issues. I have searched a lot of ways to prevent it and I have applied the next tips and it still loses often:

  • JSON for inventory saving. (Got 3 different types of items and each of them has a different Data)
  • Saving Data each 60 seconds, when someone leaves and when Bind to Close.

I’ve got like 9 datas per player and I have noticed that Data Store full queue messages doesn’t appear, or very rarely. What should I try?

2 Likes

Do you save the data in a table, sounds like a stupid question but it happens

Just to make sure you’re using pcall when saving the data in the first place right?

well 3 of the data are tables, but I code them with JSONEncode

Yeah I use pcall on every saving

Do you use UpdateAsync three times or something? For three different tables.

Im a bit confused, since data loss can manifest in many forms

Do you know what part of the data process this loss starts to occur?

I use SetAsync to save all 9 data, well sometimes 3 of them I use IncrementAsync

Show us your code, maybe we can help you that way

Not really, I have not experienced data loss myself, but i recieve like 3 reports a day of data loss

Ok so DSQueue is a table where i put the data that needs to be saved

function SaveDataWhile (player)
	DSWaitTime = 60
	
	local inventory = player:WaitForChild("inventory",10)
	local LoadingVal = inventory:WaitForChild("Loading",10)
	if LoadingVal.Value == false then
		local playerId = "Player_"..player.UserId
		local leaderstats
		local dataShavings
		local dataWins
		local TotalCharactersStatData
		local succes, errorMsg = pcall(function ()
			leaderstats = player:WaitForChild("leaderstats")
			dataShavings = leaderstats:WaitForChild("Shavings").Value
			dataWins = leaderstats:WaitForChild("Wins").Value
			TotalCharactersStatData = inventory.missions.stats.TotalCharactersStat.Value
		end)
		if not succes then
			warn(errorMsg)
		end

		local success, errorMsg = pcall(function()
			ShavingsData:SetAsync(playerId, dataShavings)
			WinsData:SetAsync(playerId, dataWins)
		end)
		if success then
			print("Shavings "..playerId.."-"..dataShavings)
			print("Wins "..playerId.."-"..dataWins)
		else
			print(player.Name.."'s shavings and wins couldnt been saved")
			warn(errorMsg)
		end
	end
	
	local an = 0
	for i, DSQueuePart in pairs(DSQueue) do
		print(DSQueuePart["name"])
		local succ, err = pcall(function ()
			DSQueuePart["data"]:SetAsync(DSQueuePart["id"], DSQueuePart["val"])
		end)
		if succ then
			print(player.Name.."'s "..DSQueuePart["name"].." Data was saved.")
		else
			warn(err)
		end
		table.remove(DSQueue,i)
	end
end

Yeah you use SetAsync 3 times, eventually that will hit a mark. I can show you a piece of code of what I use, I could improve the UpdateAsync function + the script overall, but its just an example. ( a piece became my whole script)

local dataStoreService = game:GetService("DataStoreService")
local key = "Test1"
local ds = dataStoreService:GetDataStore(key)


local stats = {
	["leaderstats"] = {
		["Stage"] = 0;
	}
}

game:GetService("Players").PlayerAdded:Connect(function(plr)
	local data = ds:GetAsync(key.."/"..plr.UserId)
	print(data)
	
	for i, StatTable in pairs(stats) do
		local folder = Instance.new("Folder")
		folder.Name = i
		folder.Parent = plr
		
		for stat, value in pairs(StatTable) do
			local val_new = Instance.new("NumberValue")
			val_new.Name = stat
			val_new.Value = value
			val_new.Parent = folder
			if data then
				val_new.Value = data[folder.Name][stat]
			end
		end
	end
	
	plr.CharacterAdded:Connect(function(char)
		local humanoid,hrp = char:WaitForChild("Humanoid"),char:WaitForChild("HumanoidRootPart")
		task.wait()
		if humanoid and hrp then
			local part = workspace.Stages:FindFirstChild(plr.leaderstats["Stage"].Value)
			hrp.CFrame = part.CFrame + Vector3.new(0,1,0)
		end
	end)
end)


local function SaveData(plr)
	local tableToSave = stats

	for i, folder in pairs(plr:GetChildren()) do
		if folder.ClassName == "Folder" then
			for i, stat in pairs(folder:GetChildren()) do
				tableToSave[folder.Name][stat.Name] = stat.Value
			end
		end
	end

	if tableToSave then
		local tries = 0
		repeat
			tries += 1
			local succ, err = pcall(function()
				local data = ds:UpdateAsync(key..'/'..plr.UserId,function(olddata)
					return tableToSave	
				end)
			end)
			if err then warn(err) task.wait(6) end
		until succ or tries == 3
		print("Data saved for " .. plr.Name)
	end
end

game:GetService("Players").PlayerRemoving:Connect(function(plr)
	SaveData(plr)
end)

game:BindToClose(function()
	for _, plr in ipairs(game:GetService("Players"):GetPlayers()) do
		coroutine.wrap(SaveData)(plr)
	end
end)
2 Likes

What i suggest is create a table and save the table in UpdateAsync. Don’t use UpdateAsync or SetAsync three times, Just do it once.

1 Like

What scares me is I see success or tries == 3… what if tries equals 3 without success, and how does this really work, since it can just be luck. But if a game has more than 200 players, SetAsync will fail, and tons of player data will get wiped.

Just use ProfileService, it’s one of the most powerful data saving modules and will handle all these headaches for you.