Global leaderboard doesn't refresh correctly

Wait, are you sure the debounce will work? I’m not much of a scripter but as far as I know people use debounces mostly on client sided local scripts. But I will try and tell you in a second.

The whole point of a debounce is as a cooldown, no matter what the use is. It fixed my issue, it might fix urs idk

The point of the leaderboard was to update every x time, when a new player joins imagine all 30 new players join at once and the leaderboard would have to update 30 times at the same moment, it might be not the best idea. However about the wait, how do you see it? I already have a task.wait but it doesn’t work. Even after waiting, the values seem to be stuck and only update when I leave and join back.

Nope. The debounce didn’t work. The values are still the same when they update. Looks like it is a different problem. However when I had the same problem on my old script I did what TomoT said and it fixed the issue, but in your script I don’t see any on player added function I could modify to add the while loop, could you maybe tell me what I can modify? I mean just look what TomoT said, it worked for the old script.

You can follow what @12345koip said.
Use the debounce and check if a player’s frame already exists before adding it

Waiting till the player’s parent is nil is essentially the same as a PlayerRemoving connection - it only fires when they leave the game. Using PlayerRemoving uses less memory.

Can you send the full new script? I’m losing track of how many changes have been made.

I have used the debounce and it didn’t work. The issue still persists, that’s the problem I had in past but TomoT fixed it. However in the new version of the script I cannot implement the same method TomoT said about so that’s a problem.

Sure here is the full script:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetOrderedDataStore("cashds_1")

local surfaceGui = script.Parent
local sample = script:WaitForChild("Sample")
local sf = surfaceGui:WaitForChild("ScrollingFrame")
local uiListLayout = sf:FindFirstChildOfClass("UIListLayout") or Instance.new("UIListLayout")
uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder
uiListLayout.Parent = sf

local db = false

local existingLabels = {}  -- Use a dictionary to keep track of existing labels

local function updateLeaderboard()
	if db then return nil end
	db = true
	print("[Global Leaderboard] Updating leaderboard at", os.date("%Y-%m-%d %H:%M:%S"))

	local top = dataStore:GetSortedAsync(false, 100)

	for _, label in ipairs(existingLabels) do
		label:Destroy()
	end

	table.clear(existingLabels)

	for rank, entry in ipairs(top:GetCurrentPage()) do
		local userId = entry.key
		local cash = entry.value

		print(`[Global Leaderboard] Player at rank {rank} with UserId {userId} has cash value {cash}`)

		local new = sample:Clone()
		new.Name = userId
		new.LayoutOrder = rank
		new.Parent = sf

		local playerNameLabel = new:FindFirstChild("playerName")
		local rankLabel = new:FindFirstChild("rank")
		local valueLabel = new:FindFirstChild("value")

		playerNameLabel.Text = "Loading..."
		rankLabel.Text = `#{rank}`
		valueLabel.Text = cash

		table.insert(existingLabels, new)

		local playerName = Players:GetNameFromUserIdAsync(userId)

		if playerName then
			playerNameLabel.Text = playerName
		else
			playerNameLabel.Text = "Unknown"
		end
	end
	db = false
end

local function save(player:Player)

	--[[
	We don't need to do all the checks here to prevent data loss.
	If this was to control the player's main data, we would
	use many more precautions. But since this store is just
	meant to mimic player data, we do not need to do that.
	]]

	local success, result = pcall(dataStore.SetAsync, dataStore, player.UserId, player.leaderstats.Cash.Value)
	if success then
		print(`Successfully saved data for @{player.Name}.`)
	else
		warn(result)
	end
end

local function autoUpdateLeaderboard()
	print("updating leaderboard")
	while true do
		local success, result = pcall(updateLeaderboard)

		if not success then
			warn("Failed to update leaderboard.")
			print(result)
		end

		task.wait(5) -- Update every 5 minutes
	end
end

task.spawn(autoUpdateLeaderboard)
Players.PlayerRemoving:Connect(save)

Did you try debugging it before testing the debounce?

That is WAY too short. Make it 5 minutes (300 seconds) at least.

I have tried to fix it, seems like all the prints and stuff work correctly and I am not sure what is wrong. The only issue now is that the leaderboard doesn’t auto-update but only updates when I leave and join back.

Why are you updating the leaderboard every 5 seconds?
Updating the leaderboard for that low time might be too frequent and can cause performance issues especially if the leaderboard has a large number of entries. Change it for a minute at least

I know, I did it only for checking purposes, it doesn’t change a thing and still the values are stuck because it does refresh and prints out the values, but they are not the same values that are in game. For example it prints i got 30 cash while i have already 32 and it updates the board correctly after i rejoin only

Then the problem might be in updating the player’s cash value in the DataStore.

That’s to do with saving data, then. Do you have an autosave at all that saves to the ordered store? Because you only save on leave right now.

For your main data, when saving, if you use an autosave (you should), make it also save to the leaderboard data stores.

Like I said previously, you might have to choose between having a leaderboard that constantly shows every single updated value, or having a slightly delayed updating one. If you delay your updating, it means that player data has better chances of saving (you are within the data budget), and you get a lot more free memory.

I had the same issue and TomoT method fixed it in past

Yes. That’s because they made it save data every 10 seconds.

Why their method is good:

  • It means the correct value is in the leaderboard data store. This can be done more efficiently.

Why it’s bad:

  • Uses a lot of memory
  • Can be incredibly slow if many players are playing
  • Uses your request budget incredibly quickly

I’m going to go back to my previous point that it doesn’t matter if your leaderboard is slightly delayed/outdated. For instance, mine updates only every 30 minutes and when a player joins. I only write to the store for it when player’s game data is being saved anyway.

Leaderboards only need to mimic data; they should not be your top priority for data-related things.

Here’s some more info about budgeting and how to manage it:
Request Budgets | Documentation - Roblox Creator Hub

I mean I get it, but the point is that guy’s method somehow worked and maybe you know how can you implement that method to your script? Even if it’s not that efficient it kinda worked and that’s what matters

Well, you would probably do something like this…

player.leaderstats.Cash:GetPropertyChangedSignal("Value"):Connect(function()
    save(player)
    updateLeaderboard() --whatever it's called
end)

Just a warning that this will slow down the server over time due to memory leaks, etc.

That’s like walking into a shop, picking up a moldy loaf of bread, and saying “it’s a loaf of bread, it’s fine”, because you don’t want to walk to the other shop and get a fresh loaf there. Yeah, it works, but it’s going to cause you issues further down the line.

It’s the same with this.
This method could cause data loss, severely impacted performance (lag) and other issues.