Ways of silencing or diminishing datastore queue requests?

So I have a game that’s been snowballing up in visits over the years. It’s not a game I’m necessarily proud of, but I continue adding updates to the small and dedicated fanbase it has.

My absolute worst issue is with datastores. I love them. I hate them. Over the past few years, I have gotten a significant number of warnings. I am aware that the warnings don’t mean anything since the saving works well. But my LORD is it a pain to see in the output.

I have tried wrapping it in pcalls and whatnot, but the warning message persists. I don’t believe that I’m calling the datastore every milisecond, either. There’s a leaderboard that refreshes every 30 seconds (and looks up to rank #1000 for what position you’re in) and the saving for the game itself only saves when you leave the game. The datastores in question are leaderboard datastores (tccdatastore, HighscoresCC_1)

edit - This leaderboard system I have works fine for one leaderboard, but I wonder if the request queue has something to do with the other leaderboard, as the search query to rank 1000 doesn’t work there.

any suggestions to a more efficient method to the above or removing the warnings would be much appreciated.


To the young children looking at this post in twenty years, there is no way of removing them. Just optimize the code in other ways.

You can’t really get rid of them because they are there for a reason. You’re definetly using the DataStores in an inefficient way. It would be nice if you could share your code for the leaderboards.

Maybe add a task.wait(0.5) between the requests?

Woah. That explains a lot.

First I’m going to say that you are using return in your loops when it seems to me that you should be using continue.

You are calling GetSortedAsync so many unnecessary times that it scares me. Why are you calling GetSortedAsync for every player? You could should call it once outside the loop.

Additionally you’re calling GetAsync on the OrderedDataStore many times, consider caching that as well.

Also note GetNameFromUserIdAsync should be pcalled, you can’t just pcall the entire function and expect it to work; even if one asynchronous method fails, the entire function will stop working. Fix: use pcall on every single asynchronous method instead of just one for the entire function.

Okay well no, you cannot silence the requests. The reason why is probably because they are so vital to your game and not being able to get/set data could possibly break it, that Roblox must let you know if there are issues.

return exits the whole function, so if you put it inside a loop, it’ll stop the entire leaderboard update. continue just skips to the next item in the loop. So for skipping invalid entries, you should use continue.

GetSortedAsync() always starts at the first page, which is why you should only call it once, and then use AdvanceToNextPageAsync() to loop through the pages as needed.

I’m sure you just could call it once and then have a table that is filled with all the data and then loop through that table for each player. Example:

local leaderboardData = {}
local pages = store:GetSortedAsync(false, 100)
repeat
    local currentPage = pages:GetCurrentPage()
    for rank, data in ipairs(currentPage) do
        leaderboardData[tonumber(data.key)] = {
            rank = rank,
            value = data.value
        }
    end
    if not pages.IsFinished then
        pages:AdvanceToNextPageAsync()
    end
until pages.IsFinished

for _, player in pairs(game.Players:GetPlayers()) do
    --go through the leaderboardData table and match it with the player's userId
end

Use DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType) in conjunction with a single-worker task queue (aka serial wave save queue) and adjust your limits accordingly.

Example:

-- Single‑Worker Save Queue Example (serial task queue)

local DataStoreService = game:GetService("DataStoreService")
local MyDataStore      = DataStoreService:GetDataStore("PlayerData")

-- Our queue of pending saves
local SaveQueue = {}
local IsProcessing = false

-- Enqueue a save request
local function EnqueueSave(userId: number, data: table)
    -- push to the end of the queue
    SaveQueue[#SaveQueue + 1] = {
        Key  = "Player_" .. userId,
        Data = data,
    }

    -- start the processor if it's not already running
    if not IsProcessing then
        IsProcessing = true
        task.spawn(ProcessQueue)
    end
end

-- The single worker that processes the queue
function ProcessQueue()
    while #SaveQueue > 0 do
        -- check how many writes we have budget for
        local budget = DataStoreService:GetRequestBudgetForRequestType(
            Enum.DataStoreRequestType.SetAsync
        )

        if budget > 0 then
            -- pull the oldest request
            local request = table.remove(SaveQueue, 1)

            local success, err = pcall(function()
                MyDataStore:SetAsync(request.Key, request.Data)
            end)

            if not success then
                warn("Save failed for", request.Key, "– requeuing:", err)
                -- put it back at the front for retry
                table.insert(SaveQueue, 1, request)
                -- back off a bit before retry
                task.wait(1)
            end
        else
            -- no budget left, wait for budget to replenish
            task.wait(0.1)
        end

        -- yield so we don’t block other tasks
        task.wait()
    end

    IsProcessing = false
end

-- Example usage:
-- whenever you want to save:
-- EnqueueSave(player.UserId, {Level = 5, Coins = 120})

I do suggest though utilizing some public modules like ProfileStore, they take away the pain of datastores with a much friendlier and easier system of just mutable tables. It handles the rest.

Well yeah that would work but it is ignoring the underlying issue of the inefficient code. It would also take the script much longer to process the data

Try something like this to replace the leaderboardupdate function (well he can but yeah)

local DataStoreService = game:GetService("DataStoreService")

-- Wait until we have at least one read‐budget before proceeding
local function waitForReadBudget()
    while DataStoreService:GetRequestBudgetForRequestType(
        Enum.DataStoreRequestType.GetAsync
    ) < 1 do
        task.wait(0.1)
    end
end

function leaderboardupdate(which, total, storeName)
    local store = DataStoreService:GetOrderedDataStore(storeName)

    -- Helper to fetch a sorted page
    local function fetchPage()
        waitForReadBudget()
        return store:GetSortedAsync(false, 100)
    end

    -- Helper to fetch a single value
    local function getValue(userId)
        waitForReadBudget()
        return store:GetAsync(userId)
    end

    pcall(function()
        -- 1) Pull the top entries
        local pages     = fetchPage()
        local topEntries = pages:GetCurrentPage()

        -- Clear out old UI
        local rankedGui = which.Gui.Ranked
        local children  = rankedGui:GetChildren()
        for i = 1, #children do
            local v = children[i]
            if not v:IsA("UIListLayout") then
                v:Destroy()
            end
        end
        task.wait(2)

        -- Populate the top‐N
        for rank = 1, #topEntries do
            if rank > total then break end
            local data = topEntries[rank]
            local id   = tonumber(data.key)
            if not id or id <= 0 then break end

            local username = game.Players:GetNameFromUserIdAsync(id)
            if not username then break end

            -- Award badge if they don’t have it
            if game.Players:FindFirstChild(username)
               and not game:GetService("BadgeService")
                             :UserHasBadgeAsync(id, 1368874078045844)
            then
                game.ReplicatedStorage.Badges:Fire(id, 1368874078045844)
            end

            local chart = which.Gui.Template:Clone()
            if which == workspace.LeaderboardCC then
                chart.Cubes.Text = data.value / 100
            else
                chart.Cubes.Text = convNumberLEADERBOARD(data.value)
            end
            chart.Rank.Text     = tostring(rank).."."
            chart.Username.Text = username
            chart.Visible       = true
            chart.Parent        = rankedGui
        end

        -- 2) Send each player their own position/value
        local players = game.Players:GetPlayers()
        for _, plr in players do
            local found = false
            local iterPages = fetchPage()

            -- Search up to 10 pages for their entry
            for _ = 1, 10 do
                local pageData = iterPages:GetCurrentPage()
                for j = 1, #pageData do
                    local entry = pageData[j]
                    if tonumber(entry.key) == plr.UserId then
                        local value = getValue(plr.UserId)
                        local displayRank  = tostring(j).."."
                        local displayValue = (which == workspace.LeaderboardCC)
                            and (value/100)
                            or convNumberLEADERBOARD(value)

                        local event = (which == workspace.LeaderboardCC)
                            and game.ReplicatedStorage.LeaderboardPositions2
                            or game.ReplicatedStorage.LeaderboardPositions

                        event:FireClient(plr, which, displayRank, displayValue)
                        found = true
                        break
                    end
                end
                if found then break end

                waitForReadBudget()
                iterPages = iterPages:AdvanceToNextPageAsync()
            end

            -- If still not found, default to “1001+”
            if not found then
                local value = getValue(plr.UserId)
                local displayValue = (which == workspace.LeaderboardCC)
                    and (value/100)
                    or convNumberLEADERBOARD(value)

                game.ReplicatedStorage.LeaderboardPositions
                    :FireClient(plr, which, "1001+", displayValue)
            end
        end
    end)
end

The issue is that once you run out of pages, your call to AdvanceToNextPageAsync() can return nil, and on the next loop you do iterPages:GetCurrentPage() on that nil value. I kinda missed it myself. Just add a guard to that.

Its messier in practice I was just prettying it up for presentation purposes lol

I admit though I wanna do other things than be on the devforums (ive been helping people for about an hour now) so i can maybe reply tomorrow or later tonight if the issue is still plaguing you

Haha all good. I figured it out myself. Can’t express words of gratitude for helping me out today. Enjoy your evening.
@7eoeb bless you as well for your support.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.