Datastore errors


Not sure what’s causing it, or why this is happening. It has something to do with my global leaderboards though. There’s 4 of them.

Here’s the script that runs on StarterPlayer

local player = game.Players.LocalPlayer

local replicatedStorage = game:GetService('ReplicatedStorage')
local functions = replicatedStorage:WaitForChild('Functions')
local uploadWinsBoard = functions:WaitForChild('UploadWinsBoard')
local uploadTagsBoard = functions:WaitForChild('UploadTagsBoard')
local uploadCashBoard = functions:WaitForChild('UploadCashBoard')
local uploadLevelBoard = functions:WaitForChild('UploadLevelBoard')

while true do
	uploadWinsBoard:InvokeServer()
	uploadTagsBoard:InvokeServer()
	uploadCashBoard:InvokeServer()
	uploadLevelBoard:InvokeServer()
	wait(600)
end

I figured with wait(600) would mean it wouldn’t fire too much, but apparently it does :man_shrugging:

You may need to give an example of the Script on the server that’s being invoked. Without the actual SetAsync or other Datastore operations, it’s difficult to pinpoint the issue.

https://www.robloxdev.com/articles/Datastore-Errors – This page is useful to pinpoint errors

I think the issue is that you are trying to use SetAsync with the same key to quickly.
You have to wait 6 seconds between write requests as stated in the article above.

This is inside the ServerScript

function uploadWinsBoard.OnServerInvoke(player)
	if player then
		local myData = playerData[player.UserId]
		if myData then
			local orderedDataWins = dataStoreService:GetOrderedDataStore('OrderedDataWins')
			orderedDataWins:SetAsync(player.Name, myData.wins)
		end
	end
end

function uploadTagsBoard.OnServerInvoke(player)
	if player then
		local myData = playerData[player.UserId]
		if myData then
			local orderedDataTags = dataStoreService:GetOrderedDataStore('OrderedDataTags')
			orderedDataTags:SetAsync(player.Name, myData.tags)
		end
	end
end

function uploadCashBoard.OnServerInvoke(player)
	if player then
		local myData = playerData[player.UserId]
		if myData then
			local orderedDataCash = dataStoreService:GetOrderedDataStore('OrderedDataCash')
			orderedDataCash:SetAsync(player.Name, myData.totalCash)
		end
	end
end

function uploadLevelBoard.OnServerInvoke(player)
	if player then
		local myData = playerData[player.UserId]
		if myData then
			local orderedDataLevel = dataStoreService:GetOrderedDataStore('OrderedDataLevel')
			orderedDataLevel:SetAsync(player.Name, myData.level)
		end
	end
end

And it’s not possible that an exploiter would be firing those functions maliciously? That’d be a very simple explanation.

Either way you probably should not use a remote for this. The Server isn’t getting any new info from the players, you might as well just use a Server Script to loop through all players with a wait() in between each player. The possibility exists otherwise that all player scripts manage to be in sync, which means that even though it’s every 600 seconds, after that period a spam of 4 requests per player is overwhelming the server.

This doesn’t look like terribly high usage to me, are there any other places in your game where you use Datastores?

Yea, for saving the players data, but that’s only called upon when joining the game, and set once you leave. The only reason I have reason to believe that this was causing it was because its for leaderboards, and the leaderboards weren’t loading. When I had 2 leaderboards everything worked fine, but now that there’s 4 it doesnt want to work

If you are saving data when someone joins a game, you are technically sending 5 requests. One for the first person who joins the game and then the four that the global leaderboards use. All it would take then is another person to join the game at the same time as the first person or someone to join and leave immediately.

Do you know when this error normally occurs? Is it when a server starts up and a lot of people join at once or does it also happen when people join gradually? Does it happen even when no new players are joining the server?

The datastore set limit is (60 + numPlayers × 10) per minute, so there doesn’t seem to be a reason why it would be hitting the limit with only 4 sets every 10 minutes for global leaderboards. Are you sure your client side script is only running once per client and isn’t in StarterGui or somewhere else where it might be running each respawn?

Yea, it’s inside StarterPlayerScripts.

It seemed to work when I played on my own, but I put sponsors up, so there’s been a constant 10-20 players on, and joining pretty quickly. But I wouldn’t think having 10 players would cause a problem when there a games with more leaderboards and 1000x more players

It seems like you do everything in a way that should work. Do you know if this might instead be caused by GetAsync or GetSortedAsync requests for the leaderboards? How do you do that?

The limits on GetSortedAsync (5 + numPlayers × 2 per minute) could explain why you had no issue with two leaderboards and also why there is no key listed in the error messages.

local dataStoreService = game:GetService('DataStoreService')
local orderedDataWins = dataStoreService:GetOrderedDataStore('OrderedDataWins')
local orderedDataTags = dataStoreService:GetOrderedDataStore('OrderedDataTags')
local orderedDataCash = dataStoreService:GetOrderedDataStore('OrderedDataCash')
local orderedDataLevel = dataStoreService:GetOrderedDataStore('OrderedDataLevel')

local replicatedStorage = game:GetService('ReplicatedStorage')
local events = replicatedStorage:WaitForChild('Events')
local updateWinsBoard = events:WaitForChild('UpdateWinsBoard')
local updateTagsBoard = events:WaitForChild('UpdateTagsBoard')
local updateCashBoard = events:WaitForChild('UpdateCashBoard')
local updateLevelBoard = events:WaitForChild('UpdateLevelBoard')

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local pagesWins = orderedDataWins:GetSortedAsync(false, 100)
		local dataWins = pagesWins:GetCurrentPage()
		updateWinsBoard:FireClient(player, dataWins)
		
		local pagesTags = orderedDataTags:GetSortedAsync(false, 100)
		local dataTags = pagesTags:GetCurrentPage()
		updateTagsBoard:FireClient(player, dataTags)
		
		local pagesCash = orderedDataCash:GetSortedAsync(false, 100)
		local dataCash = pagesCash:GetCurrentPage()
		updateCashBoard:FireClient(player, dataCash)
		
		local pagesLevel = orderedDataLevel:GetSortedAsync(false, 100)
		local dataLevel = pagesLevel:GetCurrentPage()
		updateLevelBoard:FireClient(player, dataLevel)
	end)
end)

I think it might be because of the CharacterAdded? but I need that because when you reset the leaderboards disappear

Yeah, this looks like your problem. 4 GetSortedAsync requests for each character spawn will blow through your budget pretty quickly. I would recommend fixing this by having a function that caches each GetSortedAsync request for a minute. You simply save the result of requests in a table and if someone else needs it within a minute give them the data from the table.

e.g

local getFirstPageCache = {}

function isFresh(time)
  return tick() - time < 60
end

function getFirstPage(orderedDataStore)
  if getFirstPageCache[orderedDataStore] and isFresh(getFirstPageCache[orderedDataStore].Updated) then
    return getFirstPageCache[orderedDataStore].Page
  end 
  local pages = orderedDataStore:GetSortedAsync(false, 100)
  local firstPage = pages:GetCurrentPage()
  getFirstPageCache[orderedDataStore] = {
    Updated = tick(),
    Page = firstPage,
  }
  return firstPage
end

game.Players.PlayerAdded:Connect(function(player)
  player.CharacterAdded:Connect(function(character)
    local dataWins = getFirstPage(orderedDataWins)
    updateWinsBoard:FireClient(player, dataWins)

    ect.

Another way of doing this would be by using one loop that gets the data every minute and updated a variable. This way you would never need to wait for data in the CharacterAdded function, you could always just send the data from the last request.

1 Like

Ain’t working :confused: it works when the player joins, but when they reset the boards just go back to blank. SHould I add a print somewhere that prints a certain value to see what could be going wrong?

Of course. There are two things that could be going wrong:

  1. getFirstPage is returning the wrong value when hitting the cache.
  2. The fact that the data is sent to the client without any waiting causes a timing issue.

I seemed to have fixed it. I added a repeat wait() until player.Character after the CharacterAdded function, as I’ve had problems before where CharacterAdded fires before the character is added (seems to be a bug on Roblox’s end?) But yea, seems to be fixed now

1 Like