Roblox is supposed to have different request budgets for various DataStore request types. For instance, I can call SetAsync 50 times without interfering with my pool for GetAsync. GetAsync seems to be using both GetAsync and UpdateAsync’s pool though. If I call GetAsync 50 times, I’ll lose 50 requests from my UpdateAsync budget.
This happens 100% of the time on production. I do not know how long this has been happening for. The issue can be reproduced by running the following code in the in-game developer console or through the command bar in Studio for a game that has Studio API access enabled:
local dsService = game:GetService("DataStoreService")
local datastore = dsService:GetDataStore("Test")
print("GET", dsService:GetRequestBudgetForRequestType("GetAsync"))
print("UPDATE", dsService:GetRequestBudgetForRequestType("UpdateAsync"))
print("SET", dsService:GetRequestBudgetForRequestType("SetIncrementAsync"))
for i=1,50 do
datastore:GetAsync("Test"..tostring(i))
end
print("GET", dsService:GetRequestBudgetForRequestType("GetAsync"))
print("UPDATE", dsService:GetRequestBudgetForRequestType("UpdateAsync"))
print("SET", dsService:GetRequestBudgetForRequestType("SetIncrementAsync"))
Just a guess that this might be an implementation problem since UpdateAsync gets the old value internally it may be accidentally counting towards GetAsync.
I checked all the DataStore methods/budgets and this happens in a few other places:
Repro code
local dsService = game:GetService("DataStoreService")
local ds1 = dsService:GetOrderedDataStore("Test")
local ds2 = dsService:GetDataStore("Test")
local datastores = {ds1, ds2}
local methods = {"GetAsync", "SetAsync", "IncrementAsync", "UpdateAsync", "RemoveAsync"}
function getBudgets()
local budgets = {}
for _,item in pairs(Enum.DataStoreRequestType:GetEnumItems()) do
budgets[item.Name] = dsService:GetRequestBudgetForRequestType(item)
end
return budgets
end
function checkBudgetUsage(datastore, method, startBudget, finishBudget)
local usedBudgets = {}
local usageAmounts = {}
for budget,value in pairs(startBudget) do
if finishBudget[budget] < value then
table.insert(usedBudgets, budget)
table.insert(usageAmounts, "["..tostring(budget)..": Start("..value..") Finish("..finishBudget[budget]..")]")
end
end
print("Usage for", datastore.ClassName, "with method", method..":", table.concat(usedBudgets, ", "))
print("Usage amounts", table.concat(usageAmounts, ", "))
end
for _,datastore in pairs(datastores) do
for _,method in pairs(methods) do
local start = getBudgets()
for i=1,10 do
local keyName = "Test_"..datastore.ClassName.."_"..method.."_"..tostring(i)
datastore[method](datastore, keyName, method=="UpdateAsync" and function() return i end or i)
end
local finish = getBudgets()
checkBudgetUsage(datastore, method, start, finish)
end
local start = getBudgets()
for i=1,5 do
datastore:OnUpdate("Test_"..datastore.ClassName.."_OnUpdate_"..tostring(i), function() end)
end
local finish = getBudgets()
checkBudgetUsage(datastore, "OnUpdate", start, finish)
end
local start = getBudgets()
for i=1,5 do
local datastore = dsService:GetOrderedDataStore("Test_"..tostring(i))
datastore:GetSortedAsync(false, 10)
end
local finish = getBudgets()
checkBudgetUsage(ds1, "GetSortedAsync", start, finish)
Update uses up the budget from the Get/Set pool which is understandable for the reasons @sparker22 mentioned, but GetAsync should not take requests from UpdateAsync’s pool (original issue). GetAsync does not trigger any updates.
The Wiki documentation should be updated for UpdateAsync using Get/Set’s budget, and Get should be updated to not use requests from Update’s budget.