How to Handle Saving Multiple Datastores on Player Leave?

According to the Data Stores documentation, the cooldown on set requests for the same key is 6 seconds.

When a player is leaving the game, I am saving two separate datastores via UpdateAsync. ExperienceStore and GeneralDataStore

The issue is that requests are getting added to the datastore request queue since both datastores are trying to save upon the player leaving the game.

Is there a reliable way to save multiple datastores as the player leaves without the setting cooldown causing fighting amongst the different stores trying to set their data?

You could probably do something along the lines of this I think

game.Players.PlayerRemoving:Connect(function()
        task.spawn(function()
                saveFirstData()
                wait(6)
                saveSecondData()
        end)
end)


According to the Roblox API, the cooldown is 6 seconds for the same key. Meaning, you can save one data store every 6 seconds. But if you want to save all of them at the same time, you can do it simply.

game.Players.PlayerRemoving:Connect(function(Player)
    datastore1:SetAsync(key, ...)
    datastore2:SetAsync(key, ...)
end)

Well in my Game, I am really over the limit, but I think you solved your problem yourself:

for the same key

And, you save with different keys.

Another solution are: Tables. You need only one Key, and it’s easy to use.
Edit: Haven’t seen the guy above me how wrote that too

Just store your data as a table in one DataStore, this way you won’t reach any limits.

you would save the player data in a single table here is some demo code

local playerLoaded = {}

game.Players.PlayerAdded:Connect(function(player)
    -- when the player enters the game try to load there data
    local success, value = pcall(dataStore.GetAsync, dataStore, player.UserId)
    if success == false then return end
   
    -- if the value = nil then set value to a empty table
    value = value or {}

    -- clone the data folder inside this script with all its values
    local folder = script.Data:Clone()

    -- loop all values and set them to match what was last saved in the datastore
    for i, child in ipairs(folder) do child.Value = value[child.Name] or child.Value end

    -- place the folder into the player
    folder.Parent = player

    -- set the playerloaded to true so we can keep track if there are players loaded so that bind to close can wait
    playerLoaded[player] = true
end)

game.Players.PlayerRemoving:Connect(function(player)
    -- if player loaded == nil meaning the player failed to load when they entered we simple exit because we dont want to overwrite the old data
    if playerLoaded[player] == nil then return end
 
    -- create a table and set the the values from there data folder
    local playerData = {}
    for i, child in ipairs(player.Data:GetChildren()) do
        playerData[child.Name] = child.Value
    end

    -- now save this table to the datastore
    local success, value = pcall(dataStore.SetAsync, dataStore, player.UserId, playerData)

    -- now set playerLoaded to nil so that bindtoclose can stop looping and allow the server to close
    playerLoaded[player] = nil
end)

-- while players are loaded dont close the server (only works upto 30 seconds)
game:BindToClose(function()
    while next(playerLoaded) ~= nil do
        task.wait()
    end
end)