I’m not entirely certain, I can’t find any documentation that discusses this unfortunately. These resources might interest you though: limits documentation and an informative thread found here,
In reality though, that probably doesn’t even matter. You just need to wait until you’re able to call ::GetAsync
again as @12345koip has told you.
Example Code
local DatastoreService = game:GetService('DataStoreService')
--[!] CONST
local INTERVAL_BUDGET = 1 -- time interval, in seconds, between attempts to check if we have request budget
local INTERVAL_ADVANCE = 1 -- time interval, in seconds, between attempts to get the next page
local INTERVAL_REQUEST = 7 -- time interval, in seconds, between attempts to call ::GetAsync
local MAX_PAGE_SIZE = 100 -- default page size for ::ListKeysAsync(pageSize: number)
local MAX_REQUEST_ATTEMPTS = 5 -- maximum number of request retries for a specific key
local MAX_ADVANCE_FAILURES = 5 -- maximum number of times we attempt to call ::AdvanceToNextPageAsync
--[!] UTILS
local function parseParameter(value, typing, defaultValue)
local t = typeof(typing)
if t == 'string' and typeof(value) == typing then
return value
elseif t == 'table' then
t = typeof(value)
for _, desired in next, typing do
if t == desired then
return value
end
end
end
return defaultValue
end
local function collectAllKeys(pages, page, length, results)
page = page or 0
length = length or 0
results = results or { }
local this, size, finished, attempts = nil, nil, false, 0
repeat
this = pages:GetCurrentPage()
finished = pages.IsFinished
page += 1
size = #this
table.move(this, 1, size, length + 1, results)
length += size
if not finished then
local success
while not success and attempts < MAX_ADVANCE_FAILURES do
success = pcall(pages.AdvanceToNextPageAsync, pages)
if not success then
attempts += 1
task.wait(INTERVAL_ADVANCE)
continue
end
attempts = 0
end
end
until finished or attempts >= MAX_ADVANCE_FAILURES
if attempts >= MAX_ADVANCE_FAILURES then
warn(string.format('Failure attempts exceeded when attempting to ::AdvanceToNextPageAsync at page %d', page))
end
return { Keys = results, TotalPages = page, Length = length }
end
local function getDatastoreKeys(name, scope, prefix, pageSize, cursor, excludeDeleted)
name = parseParameter(name, 'string', '')
scope = parseParameter(scope, 'string', nil)
prefix = parseParameter(prefix, 'string', nil)
cursor = parseParameter(cursor, 'string', nil)
excludeDeleted = parseParameter(cursor, 'boolean', false)
pageSize = parseParameter(pageSize, 'number', MAX_PAGE_SIZE)
pageSize = math.floor(pageSize + 0.5)
local success, store, pages, results
success, store = pcall(DatastoreService.GetDataStore, DatastoreService, name, scope)
if not success then
return false, string.format('Failed to call ::GetDataStore(name: %q, scope: %q), got exception: %s', name, scope, tostring(store))
end
success, pages = pcall(store.ListKeysAsync, store, prefix, pageSize, cursor, excludeDeleted)
if not success then
return false, string.format(
'Failed to call ::ListKeysAsync(store: %s, prefix: %q, pageSize: %d, cursor: %q, excludeDeleted: %s), got exception: %s',
store, prefix or '[NULL]', pageSize, cursor or '[NULL]', tostring(excludeDeleted), tostring(pages)
)
end
success, results = pcall(collectAllKeys, pages)
if not success then
return false, string.format(
'Failed to call retrieve pages from Datastore<name: %q, scope: %q>, got exception: %s',
name, scope or '[NULL]', tostring(results)
)
end
results.Store = store
results.PageSize = pageSize
return true, results
end
local function iterateDatastoreKeys(results)
local keys = results.Keys
local size = results.PageSize
local length = results.Length
local item, page, position = nil, 1, 1
return coroutine.wrap(function ()
while position <= length do
page = math.ceil((position - 1) / size) + 1
item = keys[position]
if not item then
break
end
coroutine.yield(page, position, item.KeyName)
position += 1
end
end)
end
local function tryGetKeyValue(store, keyName)
local budget = DatastoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.GetAsync)
while budget < 1 do
task.wait(INTERVAL_BUDGET)
end
local attempts = 0
while attempts < MAX_REQUEST_ATTEMPTS do
local success, result = pcall(store.GetAsync, store, keyName)
if success then
return true, result
end
attempts += 1
task.wait(INTERVAL_REQUEST)
end
return false, nil
end
--[!] EXAMPLE USAGE
local success, results = getDatastoreKeys('SomeDatastoreName')
if not success then
return warn(results or 'Unknown error occurred')
end
local store = results.Store
for pageNumber, keyIndex, keyName in iterateDatastoreKeys(results) do
local succ, res = tryGetKeyValue(store, keyName)
if not succ then
-- failed to get result from `store::GetAsync(key: keyName)`
continue
end
if res then
-- do whatever you want with the player's data ...
print(string.format(
'Row<key: %q, dataType: %s, page: %d, index: %d>',
keyName, typeof(res), pageNumber, keyIndex
))
end
end
I had decided to get the keys from each of the pages in advance in this example, just in case you wanted to store the key names somewhere to do this in batches. Though, it should be noted that I’m not completely certain that there isn’t a throttle on the ::AdvanceToNextPageAsync
method so you may have to change this example if that’s the case.