How to delay SetAsync calls to avoid sending too many requests?

Hey everyone, I have this run everytime a player hatches an egg so that it registers for the leaderboards:

local success, result = pcall(function() 
			EggsDS:UpdateAsync(tostring(player.UserId), math.round((Profiles[player].Data.EggsHatched)) )			
end)	

The problem is, there are multiple users opening eggs all the time and I am getting thousands of:

Is there any way to delay / wait / pospone the set asyncs so that this does not happen? maybe save every 5 - 10 seconds instead of every time an egg is opened?

Any help is really appreciated, thanks!

Create a Queue yourself to time when to fire a request, or just store their data in Sessions and then Autosave the data.

Bad Idea, you would be sending hundreds of requests, instead have the leaderboard refresh every 10 - 30 seconds, with the data (as stated above) being saved.

You mean to like not setasync there but have a loop somewhere else that waits as much time as I want and then set asyncs for the things I need there?

Use a cache system.
Profileservice has this built-in (highly recommended for player data).

The idea is that you have a local copy of the data, which you can get/set as often as you want. Then you periodically save this to the datastore.

Could you expand on this? Where can I find the API or a tutorial for this

I made a data store script recently that uses a “Caching” system, both for current data and data that failed to save when a player leaves(If it does happen).

--> Services
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--> Remotes
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local DataEvent = Remotes:WaitForChild("Data")
local DataFunc = Remotes:WaitForChild("Data_F")
local DataBind = Remotes:WaitForChild("Data_B")
local DataBindFunc = Remotes:WaitForChild("Data_B_F")

--> Data Stores
local DataStore = DataStoreService:GetDataStore("PlayerData", "UserData")

--> Data Template
local Template = {
	Time = 0,
	Gears = {},
	Gradients = {},
	Equipped = {
		Gradient = "",
		Title = "",
	},
	Titles = {},
	Kills = 0,
	ProductsBought = {},
}

--> Profiles
local Profiles = {}
local Cache = {}

--> Functions
local function GetData(player: Player)
	return Profiles[player]
end

local function LoadData(player: Player)
	local UserId = tostring(player.UserId)

	local Data = nil

	pcall(function()
		Data = DataStore:GetAsync(UserId)
	end)

	if Data == nil then
		Data = Template
	end

	return Data
end

local function SaveData(player: Player, Data: {})
	local UserId = tostring(player.UserId)

	local succ, err = pcall(function()
		DataStore:SetAsync(UserId, Data)
	end)

	if succ == false then
		Cache[UserId] = Data
	end
end

local function PlayerAdded(player: Player)
	local Data = LoadData(player)

	Profiles[player] = Data

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local Time = Instance.new("NumberValue")
	Time.Name = "Time"

	Time.Value = Data.Time

	Time.Parent = leaderstats
	leaderstats.Parent = player

	task.spawn(function()
		while task.wait(60) do
			Profiles[player].Time += 1
			Time.Value = Profiles[player].Time
		end
	end)
end

local function PlayerRemoving(player: Player)
	local Data = Profiles[player]

	SaveData(player, Data)

	Profiles[player] = nil
end

local function onEvent(player: Player, Key: string, ...)

end

local function onBind(key: string, ...)

end

local function onBindFunc(key: string, ...)
	if key:lower() == "get" then
		return GetData(...)
	end

	return nil
end

local function onFunc(player: Player, Key: string, ...)
	if Key:lower() == "get" then
		return GetData(player)
	end

	return nil
end

--> Race Conditions (If a player loads in before this script runs.)
for _, v in pairs(Players:GetPlayers()) do
	PlayerAdded(v)
end

--> Connections
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)

--> Remotes
DataEvent.OnServerEvent:Connect(onEvent)
DataFunc.OnServerInvoke = onFunc
DataBind.Event:Connect(onBind)
DataBindFunc.OnInvoke = onBindFunc

--> Game Shutdown
game:BindToClose(function()
	for _, v in pairs(Players:GetPlayers()) do
		PlayerRemoving(v)
	end
	
	--> Also save the data of players that failed to save.
	for UserId, Data in pairs(Cache) do
		DataStore:SetAsync(UserId, Data)

		Cache[UserId] = nil
	end
end)

You would use remotes to retrieve the players data from other scripts, and from the client.

An example of getting a players data with all the listed remotes in the variables:

DataFunc: (Client)

--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--> remotes
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local DataFunc = Remotes:WaitForChild("Data_F")

--> Added Wait
task.wait(1)

--> Get Player Data
local PlayerData = DataFunc:InvokeServer("get")

--> Print Player Data
print(PlayerData)

DataBindFunc: (Server)

--> Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--> Remotes
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local DataBindFunc = Remotes:WaitForChild("Data_B_F")

--> Functions

Players.PlayerAdded:Connect(function(player: Player)
	--> wait so that data can load
	task.wait(3)
	
	--> Get Player Data
	local PlayerData = DataBindFunc:Invoke("get", player)
	
	--> Print Data
	print(PlayerData)
end)

But like someone previously mentioned, ProfileService is a much better data store that uses a cache.
Profile Service: