Datastore delay?

So, the server doesn’t print both of my test account in studio mode when I leave the game, they print like a few seconds after… This is kinda strange when when I have a queue for it. Help needed please

local Scope = "Version1"

local DataStore = game:GetService("DataStoreService"):GetDataStore("Stats", Scope)
local Event = game:GetService("ReplicatedStorage"):FindFirstChild("Events"):FindFirstChild("DATASTORE_UPDATE")
local PlayerService = game:GetService("Players")
local Main_Folder = Instance.new("Folder")
Main_Folder.Name = "DataStore"
Main_Folder.Parent = game:GetService("ServerScriptService")

-- For datastore
local Queue = {}
local Cooldown = 6
local Debounce = false

local function Save()
	for e, v in pairs(Queue) do
		local succ, err = pcall(function()
			DataStore:SetAsync(e, {
				["Total Wins"] 			= v["Total Wins"],
				["Total Deaths"]		= v["Total Deaths"],
				["Best Killstreak"]		= v["Total Killstreak"]
			})
		end)
		if succ then
			print("Saved data for", e)
		else
			print(err)
		end
	end
	Queue = {}
end

PlayerService.PlayerAdded:Connect(function(Player)
	local Folder = Instance.new("Configuration")
	Folder.Name = Player.Name
	Folder.Parent = Main_Folder
	
	local Wins_Folder = Instance.new("IntValue")
	Wins_Folder.Name = "Total Wins"
	Wins_Folder.Parent = Folder
	
	local Deaths_Folder = Instance.new("IntValue")
	Deaths_Folder.Name = "Total Deaths"
	Deaths_Folder.Parent = Folder
	
	local Best_Killstreak_Folder = Instance.new("IntValue")
	Best_Killstreak_Folder.Name = "Best Killstreak"
	Best_Killstreak_Folder.Parent = Folder
	
	local SSS = game:GetService("ServerScriptService")
	local Folder = SSS:FindFirstChild("DataStore")
	
	wait(Cooldown)
	
	--[[ > > > Loading stats < < < ]]--
	
	local succ, err = pcall(function()
		local Score = DataStore:GetAsync(Player.UserId) or {
			0,
			0,
			0
		}
		Wins_Folder.Value = Score["Total Wins"]
		Deaths_Folder.Value = Score["Total Deaths"]
		Best_Killstreak_Folder.Value = Score["Best Killstreak"]
	end)
	
	if succ then
		print("Loaded data for", Player.UserId)
	else
		print("Failed loading data for", Player.UserId, ":", err)
	end
	
	Event:FireClient(Player, Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Wins").Value, "WINS")
	Event:FireClient(Player, Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Deaths").Value, "DEATHS")
	Event:FireClient(Player, Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Best Killstreak").Value, "KILLSTREAK")
	
	--[[ > > > Update KD when player joins < < < ]]--
	
	local Update_Wins = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Wins")
	local Update_Deaths = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Deaths")
	local updatekd = Update_Wins.Value / (Update_Deaths.Value == 0 and 1 or Update_Deaths.Value)
	Event:FireClient(Player, math.floor(updatekd * 100 + 0.5) / 100, "K/D RATIO")

	--[[ > > > Get their stats < < < ]]--
	
	local Wins_Changed = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Wins")
	Wins_Changed:GetPropertyChangedSignal("Value"):Connect(function()
		Event:FireClient(Player, Wins_Changed.Value, "WINS")
		local Update = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Wins").Value
		local kd = Update / (Wins_Changed.Value == 0 and 1 or Wins_Changed.Value)
		Event:FireClient(Player, math.floor(kd * 100 + 0.5) / 100, "K/D RATIO")
	end)
	
	local Deaths_Changed = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Deaths")
	Deaths_Changed:GetPropertyChangedSignal("Value"):Connect(function()
		Event:FireClient(Player, Deaths_Changed.Value, "DEATHS")
		local Update = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Total Wins").Value
		local kd = Update / (Deaths_Changed.Value == 0 and 1 or Deaths_Changed.Value)
		Event:FireClient(Player, math.floor(kd * 100 + 0.5) / 100, "K/D RATIO")
	end)
	
	local Best_Killstreak_Changed = Main_Folder:FindFirstChild(Player.Name):FindFirstChild("Best Killstreak")
	Best_Killstreak_Changed:GetPropertyChangedSignal("Value"):Connect(function()
		Event:FireClient(Player, Best_Killstreak_Changed.Value, "KILLSTREAK")	
	end)
	
	--[[ > > > Save when player leaves < < < ]]--
	
	PlayerService.PlayerRemoving:Connect(function(Player)
		Queue[Player.UserId] = {
			["Total Wins"] 			= Wins_Folder.Value,
			["Total Deaths"]		= Deaths_Folder.Value,
			["Best Killstreak"]		= Best_Killstreak_Folder.Value
		}
		while Debounce do
			wait(1)
		end
		Debounce = true
		Save()
		wait(Cooldown)
		Debounce = false
	end)
	
	--[[ > > > Save if shutdown occurs < < < ]]--
		
	game:BindToClose(function()
		for _,v in pairs(PlayerService:GetPlayers()) do
			Queue[v.UserId] = {
				["Total Wins"] 			= Wins_Folder.Value,
				["Total Deaths"]		= Deaths_Folder.Value,
				["Best Killstreak"]		= Best_Killstreak_Folder.Value
			}
			while Debounce do
				wait(1)
			end
			Debounce = true
			Save()
			wait(Cooldown)
			Debounce = false
		end
	end)
end)

What part specifically isn’t working? It’s hard to help you resolve your issue when you state a scenario and throw your entire script into the thread.

Well mainly when you save their stats. If I left with two accs at the same time it would be a delay in the prints… It would first print the first one in the queue then the second one after like up to 1 minute in games for some strange reason.

EX:

game:BindToClose(function()
		for _,v in pairs(PlayerService:GetPlayers()) do
			Queue[v.UserId] = {
				["Total Wins"] 			= Wins_Folder.Value,
				["Total Deaths"]		= Deaths_Folder.Value,
				["Best Killstreak"]		= Best_Killstreak_Folder.Value
			}
			while Debounce do
				wait(1)
			end
			Debounce = true
			Save()
			wait(Cooldown)
			Debounce = false
		end
	end)
end)

That’s because of the yield you’re performing in the function you’re passing to BindToClose, which isn’t necessary to have (the debounces and while loop). If you aren’t writing to the same key, a 6-second cooldown is not required between all write requests. As well, until the iteration ends for one player, it does not occur for a second player.

This is typically why I do not perform any saves midgame, though at the sacrifice of chancing a failure in autosave behaviour. I simply cache data at run time via arrays. I then only load once or save three times during the entire session - one load request for when a player joins or a save request if they have no data, one when they leave and one if the server closes.

But if I save their data instantly then I don’t need a queue, right? But that would throttle then? What do you suggest?

I don’t think you need a queue whatsoever. I just save data on the fly and that seems to work out. Worst case scenario (which never seems to have happened), write requests conflict and I end up hitting the write request cooldown, in which data does not get saved due to a throttle. I just repeat the function twice with around a 3-second cool off each interval.

You would hit the cooldown if two players leaves after eachother and within 6 secs sooooo…

No you wouldn’t, because the 6-second write cooldown doesn’t occur between different keys. A write cooldown between all write requests is useless, I’d only wrap methods in a cooldown if it was to the same key. I don’t hit cooldowns if I’m saving different players’ data, since all players have their data represented by different keys.

2 Likes

Why did you make the same topic 2 times? Custom wins/death handler issue (perhaps a delay?)

That’s a different topic altogether? Please don’t post off-topic comments and check the threads you’re viewing before you post something like this.

If they join and leave quickly, then join again, wouldn’t that hit it? I implemented it because it hit the limit before…

Realistically, no. I believe the write request cooldown only applies per game server, not for all servers combined. It would be mentioned that said cooldown is a cross-server or game limitation (and if it is across games rather than per server, that’s a documentation oversight which urgently needs to be corrected).

1 Like

Ok, I’ll try that then in the future. No solution for now though…