Data getting rolled back?

My game has been experiencing data loss and it’s from data getting rolled back, this is a big problem because what if they buy gems with robux and then lose it all, or someone gives them something for free and it disappears. This is my loading data code:

--load player data
local function loadData(player)
	print(player)
	local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			return dataBase:GetAsync(player.UserId)
		end)

		attempt += 1
		if not success then
			warn(playerData)
			task.wait(1)
		end
	until success or attempt == 3

	if success then
		
		if not playerData then--give default data if they're new
			playerData = {
				["Gems"] = 0,
				["SelectedTowers"] = {"Cameraguy"},
				["OwnedTowers"] = {"Cameraguy"},
				["MaxTowers"] = 5,
				["RedeemedCodes"] = {},
				["Wins"] = 0,
				["Snowflakes"] = 0,
			}
			beamEvent:FireClient(player)
		end
		
		data[player.UserId] = playerData
		isLoaded[player] = true

	else
		warn("Unable to get "..player.UserId.."'s data")
		player:Kick("There was a problem getting your data")
	end
end

for i, randomPlr in pairs(Players:GetPlayers()) do
	loadData(randomPlr)
end
Players.PlayerAdded:Connect(loadData)

Players.PlayerAdded:Connect(function(player)
	task.wait(1)
	player.leaderstats.Wins.Value = data[player.UserId].Wins
end)

Saving data code:

--save player data
local function saveData(player)
	print(player)	
	if data[player.UserId] and player.UserId ~= 0 and isLoaded[player] == true then
		local success = nil
		local playerData = nil
		local attempt = 1

		repeat
			success, playerData = pcall(function()
				return dataBase:UpdateAsync(player.UserId, function()
					return data[player.UserId]
				end)
			end)

			attempt += 1
			if not success then
				warn(playerData)
				task.wait(1)
			end
		until success or attempt == 3

		if success then
			print("Data saved successfully")
		else
			warn("Unable to save data for player", player.UserId)
		end
	else
		warn("No session data for", player.UserId)
	end
	isLoaded[player] = nil
end

Players.PlayerRemoving:Connect(function(player)
	saveData(player)
	data[player.UserId] = nil
end)



game:BindToClose(function() --if game shuts down
	if not RunService:IsStudio() then
		print("Shutting down")
		for _, player in ipairs(Players:GetPlayers()) do
			task.spawn(function()
				saveData(player)
			end)
		end
	else
		print("Shutting down inside studio")
	end

	while not next(isLoaded) do -- we keep waiting until all player datas are saved and closed by the spawned functions
		task.wait(1)
	end
end)

Any help is appreciated since I rlly want this solved asap, thanks and have a good day!

Some more info, a player mentioned that when he joined another place of the game and joined back to the main game, he lost some of his OwnedTowers but not anything else, code for that one is like this:

local data = {}
local isLoaded = {}

--load player data
local function loadData(player)
	local success = nil
	local playerData = nil
	local attempt = 1
	
	repeat
		success, playerData = pcall(function()
		return dataBase:GetAsync(player.UserId)
		end)
		
		attempt += 1
		if not success then
			warn(playerData)
			task.wait(1)
		end
	until success or attempt == 3
	
	if success then
		print("Connection success")
		if not playerData then--give default data if they're new
			print("New player, giving default data")
			playerData = {	
				["Gems"] = 0,
				["SelectedTowers"] = {"Cameraguy"},
				["OwnedTowers"] = {"Cameraguy"},
				["MaxTowers"] = 5,
				["RedeemedCodes"] = {},
				["Snowflakes"] = 0,
			}
		end

		data[player.UserId] = playerData
		isLoaded[player] = true

		
		if player.UserId == 1317259399 then
			playerData = {
				["Gems"] = 9999999,
				["SelectedTowers"] = {"Dual Dartgun Cameraguy","Astroclaw Upgraded Supreme Cameraguy","Acidgun Upgraded Supreme Cameraguy","Handsaw Upgraded Supreme Cameraguy","Cyber Strider","Dark TV-Guy","Dart Gun Cameraguy","Flamethrower Cameraguy"},
				["OwnedTowers"] = {"Cameraguy","Speakerguy","Cameragal","TV-Guy","Saw Cameraguy","Big Cameraguy","Scientist Cameraguy","Dark Cameragal","TV-Gal","Speakergal","Big TV-Guy","Big Scientist Cameraguy","Big Speakerguy","Dark Speakerguy","Rocket Flying Cameraguy","Supreme Speakerguy","Supreme Cameraguy","Ninja Cameraguy","Supreme Cinemaguy","Upgraded Supreme Cameraguy","Upgraded Supreme Speakerguy","Upgraded Ninja Cameraguy","Upgraded Corrupted Supreme Speakerguy","Corrupted Supreme Speakerguy","Rocket Big Cameraguy","Supreme Pumpkin Sorcerer"},
				["MaxTowers"] = 5,
				["RedeemedCodes"] = {},
				["Wins"] = 0
			}
			data[player.UserId] = playerData
			
		end
		
		if playerData.Wins == nil then
			playerData.Wins = 0
		end

		if playerData.Snowflakes == nil then
			playerData.Snowflakes = 0
		end
	
	else
		warn("Unable to get data for player", player.UserId)
		player:Kick("There was a problem getting your data")
	end
end
Players.PlayerAdded:Connect(loadData)
--save player data
local function saveData(player)
	if alreadyChanged == false then
		calculation = #Players:GetPlayers()/4
		alreadyChanged = true
	end
	if data[player.UserId] and isLoaded[player] then
		local success = nil
		local playerData = nil
		local attempt = 1
		
		local info = workspace.Info
		local stars = math.round(info.Wave.Value / 2) * 5
		local win = 0
		local snowflake = 0
		
		
		
		if info.Message.Value == "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "City" then
			stars = 50
			win = 1
		end
	
		if info.Message.Value == "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "Ruins" then
			stars = 100
			win = 1

		end
			
		if info.Message.Value == "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "Toilet Base" then
			stars = 250
			win = 1

		end
		
		if info.Message.Value == "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "Alliance Base" then
			stars = 375
			win = 1

		end
		
		if info.Message.Value == "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "Toilet Canyon" then
			stars = 725
			win = 1

		end
		
		--[[if info.Message.Value == "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "Frosty Forest" then
			stars = 100
			snowflake = 750
			win = 1
		end--]]
		
		--[[if info.Message.Value ~= "VICTORY" and workspace.Map:FindFirstChildOfClass("Folder").Name == "Frosty Forest" then
			stars = 50
			snowflake = math.round(info.Wave.Value / 2) * 5
		end--]]
		
		local addition = 0.75 + calculation
		
		stars = stars * addition
		--snowflake = snowflake * addition
		
		local hours = tonumber(os.date("%H"))
		local days = tonumber(os.date("%j"))
		local month = tonumber(os.date("%m"))
		local seed = hours * days * month
		
		
		data[player.UserId].Gems += stars
		data[player.UserId].Wins += win
		data[player.UserId].Snowflakes += snowflake

		repeat
			success, playerData = pcall(function()
				return dataBase:UpdateAsync(player.UserId, function()
					return data[player.UserId]
				end)
			end)

			attempt += 1	
			if not success then
				warn(playerData)
				task.wait(1)
			end
		until success or attempt == 3

		if success then
			print("Data saved successfully")
		else
			warn("Unable to save data for player", player.UserId)
		end
	else
		warn("No session data for", player.UserId)
	end
end

exitEvent.OnServerEvent:Connect(function(player)
	saveData(player)
	data[player.UserId] = nil
end)

game:BindToClose(function() --if game shuts down
	if not RunService:IsStudio() then
		print("Shutting down")
		for _, player in ipairs(Players:GetPlayers()) do
			task.spawn(function()
				saveData(player)
			end)
		end
	else
		print("Shutting down inside studio")
	end

	while not next(isLoaded) do -- we keep waiting until all player datas are saved and closed by the spawned functions
		task.wait(1)
	end
end)

Did you run this code in Roblox Studio?

I tested in both studio and ingame but I can’t create the issue myself so it’s quite hard to get a good understanding of whats happening

You could use someone else module I don’t usually trust myself when writing a saving system, Datastore2 have back-up cache and nothing bad have happen since I used it at first even though I don’t know anything.

You could optimize your code by doing this instead:
Use a dictionary. You will write less if - then logic

local tab = {["Alliance Base"] = 375}
if info.Message.Value == "VICTORY" and tab[workspace.Map:FindFirstChildOfClass("Folder").Name] then
stars = tab[workspace.Map:FindFirstChildOfClass("Folder").Name]
win = 1
end

(Just example)

To me, this sounds like your subplace is overwriting the new one, since the subplace is grabbing data before the new one can save.

Suphi’s Datastore Module honestly solves this since it’s got session locking, but if you don’t want to use that, I’d look into that.

I changed the saving to this:

--save player data
function saveData(player)
	print(player)	
	if data[player.UserId] and player.UserId ~= 0 and isLoaded[player] == true then
		local success = nil
		local playerData = nil
		local attempt = 1

		repeat
			success, playerData = pcall(function()
				return dataBase:UpdateAsync(player.UserId, function()
					return data[player.UserId]
				end)
			end)

			attempt += 1
			if not success then
				warn(playerData)
				task.wait(2)
			end
		until success or not player.Parent
		
		if success then
			print("Data saved successfully")
		else
			warn("Unable to save data for player", player.UserId)
		end
	else
		warn("No session data for", player.UserId)
	end
	isLoaded[player] = nil
end

Players.PlayerRemoving:Connect(function(player)
	saveData(player)
	data[player.UserId] = nil
end)



game:BindToClose(function() --if game shuts down
	if not RunService:IsStudio() then
		print("Shutting down")
		for _, player in ipairs(Players:GetPlayers()) do
			task.spawn(function()
				saveData(player)
			end)
		end
	else
		print("Shutting down inside studio")
	end

	while not next(isLoaded) do -- we keep waiting until all player datas are saved and closed by the spawned functions
		task.wait(2)
	end
end)

Data is still getting rolled back though