Why won't my datastore saving system retry properly after failure

this might help

Why does he use ipairs though to loop through the players in the bindtoclose method?

If you are testing in studio it doesn’t actually wait til all threads have finished (or 30 seconds) as it assumes you might want to get on with something else!
Add a bind to close with a task.wait(20) and it should let the thread complete.

because the player’s removing function stops working as the game shutdown

I thought it might be because ipairs doesn’t continue to loop through when it finds a nil value possibly preventing error?

Also how does the player removing function not working link to using ipairs?

I was told by the same guy in a different topic not to use bindtoclose

So should I get rid of the retry system on the player removing event and move it into the gamebindtoclose?

Also what do you mean by the task.wait(20)? Can you give some code examples please?

Bind to close and player removing run at different times.
Anytime a player leaves the game you would probably want to save their data, and would likely have time to retry multiple times.
However if the server closes for another reason, you’ll probably want to try and save everyone’s data then as well! Only issue is the players might not be ‘removed’ triggering the player removing event.

Tldr, save and retry for both player.removing and bindtoclose

I assume the OP of the that just did it out of preference and since it’s a normal array used it instead

1 Like

Okay, but does taht mean that I can’t make the delay longer on the player removing event because it won’t retry all the times?

From memory you get 30 seconds once bindtoclose is triggered, so you can run retries fine, but it will depend on how many people are in the server and how fast you can push through the calls and what else you might need to run.
Most likely you could just keep retrying until the server shuts down for bind to close.

(The task.wait(20) in bindbto close was just to keep the studio server from shutting down which would prevent the retries from completing)

You could try something like this:

local serverDown = false
local function savePlayerData(player)
    local retryLimit = 5
    local retries = 0
    if serverDown then
        retryLimit = 30
    end
   repeat
    -- code for saving with retries
    until success or retries >= retryLimit
end
Players.PlayerRemoving:Connect(savePlayerData)
game.BindToClose: Connect(function()
    serverDown = true
    for i, p in game.Players do
        task spawn(function() savePlayerData(p) end)
    end
end

I would probably just retry until the call succeeds because why would I risk throttling it?

But my question is then, can I just retry with task.wait(1) on player removing event or no?

Yes. You can. However, if the datastore is unresponsive for 5 seconds and your retry limit is 5 then the data won’t save. So this is why you would slow down the request rate.

Also if the retry rate is too high, and the data save request is malformed (so would never save anyway) it’s a lot of unnecessary calls over time.

To how many seconds should I do it to then?

I use 5 retries, and pass the retry number as the wait time. (So all retries are completed in about 15 seconds)

I also use an autosave function which spreads the autosave calls from all players automatically over 2 minutes

Sadly, the gamebindtoclose function does nothing:

game:BindToClose(function()
	for _, Player in ipairs(game.Players:GetPlayers()) do
		
		local SaveData = {
			Time = Player.leaderstats.Time.Value,
			RobuxDon = Player.leaderstats.RobuxDon.Value
		}
		
		local success, errormessage = pcall(function()
			DataStore:SetAsync(Player)
		end)

		if not success then
			local Retries = 0
			repeat
				local worked = pcall(function()
					DataStore:SetAsync(Player)
				end)
				Retries += 1
				warn(Retries)
				task.wait(Retries)
			until
			worked or Retries >= 5
		end
	end
end)

Only the retry message in the playerremoved event prints only once. The bindtoclose doesn’t print at all.

I’m assuming this was a solo play test?
If the player removed event fired there wouldn’t be any players in the game when bind to close ran.
Add a print at the start of the bind to close function to check when it runs (you could print players:GetPlayers() )

Hi,

My replies have been linked twice in this topic, so i thought I’d chip in with my suggestions.

I did not say you shouldn’t use BindToClose, but rather data should not be saved on BindToClose. This is because whenever the server is shut down, Players.PlayerRemoving is fired for that player. So, should data be saved in your Players.PlayerRemoving connection and on BindToClose, you have two sets of data being written for the same player, and further requests are more likely to be dropped. Alternatively, should all the players have been kicked by the time BindToClose is run, your code within that block will also do nothing.

To fix this, you need to keep the server running for as long as possible in the game environment to ensure requests are processed. To prevent requests from also being dropped, you should create your own queue system to feed requests one by one to the store.

In one of my replies from a previous topic which has been linked here, I mentioned how data store requests are processed asynchronously, so your code continues running before data write operations have been fully processed. This causes the server to close everything once BindToClose has finished running; everything related to that game server, including data store requests, regardless of whether or not they are processed.

Try the following code in your BindToClose function. It’ll wait 5 seconds in studio and 30 seconds (the limit) in game servers.

game:BindToClose(function(_reason)
    task.wait(if game:GetService("RunService"):IsStudio() then 5 else 30)
end)

would also like to note that @Wigglyaa and @CreditsMix have both mentioned some of my points already. Thanks!

Also, if you also want to use different behaviour depending on the reason the game shut down, you can get the reason for the server shutdown as a parameter to BindToClose.

Yeah it was

Alright I will try this thank you