Datastore completely wiped

Hello everyone, I was testing my game when all my data was reset. It could have been something to do with being on super slow wifi (I was connected to a really bad hotspot), or it could have been a coincidence. Either way, I can’t figure out why this happened.

Note: This only happened to my main account. My alt account’s progress which I have just tested worked fine.

The datastore code is as follows:


-- // Assigning variables //
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("MyDataStore") -- This can be changed to whatever you want

local function saveData(player) -- The functions that saves data

	local tableToSave = {
		player.leaderstats.GoldCoins.Value; -- First value from the table
		player.BoughtFiveHourBook.Value; -- Second value from the table
		player.FiveHourBookBoostLeft.Value;
		player.leaderstats.Highscore.Value,
		player.Experience.Value;
	}

	local success, err = pcall(function()
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
	end)

	if success then -- If the data has been saved
		----print("Data has been saved!")
	else -- Else if the save failed
		----print("Data hasn't been saved!")
		warn(err)		
	end
end

game.Players.PlayerAdded:Connect(function(player) -- When a player joins the game

	-- // Assigning player stats //
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local GoldCoins = Instance.new("IntValue")
	GoldCoins.Name = "GoldCoins"
	GoldCoins.Parent = leaderstats
	
	local FiveHourBookBoostLeft = Instance.new("IntValue")
	FiveHourBookBoostLeft.Name = "FiveHourBookBoostLeft"
	FiveHourBookBoostLeft.Parent = leaderstats.Parent
	
	local BoughtFiveHourBook = Instance.new("BoolValue")
	BoughtFiveHourBook.Name = "BoughtFiveHourBook"
	BoughtFiveHourBook.Parent = leaderstats.Parent
	
	local Score = Instance.new("IntValue")
	Score.Name = "Highscore"
	Score.Parent = leaderstats
	
	local XP = Instance.new("IntValue")
	XP.Name = "Experience"
	XP.Parent = player
	

	local data -- We will define the data here so we can use it later, this data is the table we saved
	local success, err = pcall(function()

		data = dataStore:GetAsync(player.UserId) -- Get the data from the datastore

	end)

	if success and data then -- If there were no errors and player loaded the data

		GoldCoins.Value = data[1] -- Set the GoldCoins to the first value of the table (data)
		BoughtFiveHourBook.Value = data[2] -- Set the coins to the second value of the table (data)
		FiveHourBookBoostLeft.Value = data[3]
		Score.Value = data[4]
		XP.Value = data[5]

	else -- The player didn't load in the data, and probably is a new player
		----print("The player has no data!") -- The default will be set to 0
	end

end)

game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
	local success, err  = pcall(function()
		saveData(player) -- Save the data
	end)

	if success then
		----print("Data has been saved")
	else
		----print("Data has not been saved!")
	end
end)

game:BindToClose(function() -- When the server shuts down
	for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
		local success, err  = pcall(function()
			saveData(player) -- Save the data
		end)

		if success then
			----print("Data has been saved")
		else
			----print("Data has not been saved!")
		end
	end
end)

Also, I have not recently changed anything in the datastore script.

Never use DataStoreService, data loss happen all the time with it. No popular game or competent developer use DataStoreService, instead there are better alternatives.

The one I use, and my favorite one is ProfileService. By using this you can rest assured that you will never encounter any data loss ever again. They have a full documentation if you’re interested.

It depends on how you use it, using it incorrectly will cause data loss

1 Like

Not just how you use it, it would also depend on if the Roblox servers are slow or if Roblox is down, then you don’t really have a way to prevent data loss.

Did I use it incorrectly? (30 ch rs)

Been hammering the datastore for years and have never had a real problem with data errors.
Ran into a few over sends, but cleaned up my code and learned how to save that data when they exit only. While working off locals in game and not using datastore read/write constantly.

1 Like

ProfileService also just uses datastores; they just have extra stuff like session locking and backups (which by the way, are now supported directly by datastores). Roblox datastores are reliable if you use them and handle errors correctly.

1 Like

Thank you. Do you know why the datastore wiped my data though? Am I handling them wrong?

  1. There is no retrying if the get/set fails.
  2. UpdateAsync should be used over SetAsync.
  3. If all retries fail, you can get the past versions using the method in my previous reply to help minimize the damage.

Thanks. So, would retrying if the get/set fails be something like this?

game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
	local success = nil
	while not success do
		wait()
		local success, err  = pcall(function()
			saveData(player) -- Save the data
		end)

		if success then
			----print("Data has been saved")
		else
			
		end
	end
end)

Also, is the reason that the datastore was completely wiped because I was using SetAsync instead of UpdateAsync? It makes sense to me but I just want to make sure. (If I used UpdateAsync instead, the player would only lose the data from their playing session, correct?)

Pretty much.

It’s possible, since the initial attempt to load the data might’ve failed. It might’ve overwrote the data in the datastore with the default data because it didn’t check if it failed to load the data in the 1st place.

1 Like

For some reason, UpdateAsync causes an error (“unable to cast value to function”) to happen. If I change it back to SetAsync, it works just fine.

When using Update Async, you do this:

Data:UpdateAsync(Player.UserId, function()
return ItemsValue.Value
end)

How would I convert it from my existing code?

local success, err = pcall(function()
	dataStore:SetAsync(player.UserId, tableToSave)
end)

Try this maybe?

local success, err = pcall(function()
	dataStore:UpdateAsync(player.UserId, function()
          return tableToSave
    end)
end)

The data loss didn’t happen because you don’t retry or use SetAsync (popular myth).
It’s because you don’t check if the data was loaded properly so sometimes your script is saving data which didn’t load (default).
To prevent this, after making sure data has loaded properly, add loaded player to a table.
On player removing, check if the player is in this table and if they are, save the data.

1 Like

How would I make sure that the data actually didn’t load, and the player is not new? (It would look the same in both instances)

I know I could use a datastore to keep track of if a player has joined before, but that could also fail. I am thinking of using a badge to check if it’s their first time playing, but player can delete badges from their inventory. So, how would I do this?

Personally I disagree, I think there are many well coded games that uses the default data store and generally in the event of data loss, it is typically a small issue in the code.

New player: data == nil
Not loaded properly: success == false or (data and typeof(data) ~= 'table')

“not loaded properly” condition will rarely evaluate to true, most of the time it’s long request time causing your script to save the data before it loads and that’s why it’s important to save the data only when you’re sure it had loaded.
Anyway, it’s always better to protect yourself from such scenario as well.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.