New "excludeDeleted" parameter for "ListKeysAsync" doesn't work

Reproduction Steps

local DataStoreService = game:GetService("DataStoreService")
JogoStore 	= DataStoreService:GetDataStore("Jogo")

for i = 1, 3 do -- create 3 keys
	JogoStore:SetAsync(i, i*10)
end

function ListAllKeys(DS, Operation)
	print("\n***ListAllKeys(DS, Operation): ", DS, Operation)
	local DSoptions = Instance.new("DataStoreOptions")
	DSoptions.AllScopes = true
	local DataStore = DataStoreService:GetDataStore(DS, "", DSoptions)

	local Success, Pages = pcall(function()
		return DataStore:ListKeysAsync('global', nil, nil, true) -- true = excludeDeleteda
	end)
	if Success then
		while true do
			local Itens = Pages:GetCurrentPage()
			for _, Key in ipairs(Itens) do
				if Operation == 'Remove' then
					local Success, Erro = pcall(function()
						local Result = DataStore:RemoveAsync(Key.KeyName) -- remove conteúdo da chave
						print('Remove', Key.KeyName)
					end)

					if not Success then
						warn(Erro)
					end
				else
					local Value, KeyInfo = DataStore:GetAsync(Key.KeyName)
					print('Key.KeyName, Value, KeyInfo', Key.KeyName, Value, KeyInfo)
				end
			end
			if Pages.IsFinished then
				break
			end

			repeat
				local Success, Errormessage = pcall(function()
					Pages:AdvanceToNextPageAsync()
				end)
				if not Success then
					warn('Error')
					warn(Errormessage)
					wait(1)
				end
			until Success

		end
	end
end


ListAllKeys('Jogo')
ListAllKeys('Jogo', 'Remove')
ListAllKeys('Jogo')

Expected Behavior

Last week Release Notes for 565 | Roblox Creator Documentation was released where it is reported that ListKeysAsync has a new parameter to correct a bug reported 6 months ago (there is a typo there, as reported here):

The script above is exactly the same as mentioned in a bug report from 6 months ago.
The only difference that the true parameter was inserted inside the ListKeysAsync line.

Actual Behavior

As shown in the original script:

  1. 3 keys are created in a DS
  2. The 3 keys created above are listed
  3. The 3 keys are removed
  4. Even removed, the 3 keys are still listed.

Changing the line from:

return DataStore:ListKeysAsync('global')

to

return DataStore:ListKeysAsync('global', nil, nil, true)

I get this:



Issue Area: Engine
Issue Type: Other
Impact: High
Frequency: Constantly
Date First Experienced: 2023-03-02 00:03:00 (-03:00)

1 Like

Hi, still looking into the 502 errors I’m unable to repro.

However, what did you mean by ListKeysAsync does not work? Please let me know if I’m misunderstanding but it looks like the code you attached prints the results of a GetAsync call

local Value, KeyInfo = DataStore:GetAsync(Key.KeyName)
print('Key.KeyName, Value, KeyInfo', Key.KeyName, Value, KeyInfo)

What does it return when you print out the values returned by the ListKeysAsync call?

2 Likes

Actually, apparently there is something very wrong with ListKeysAsync.

I’ve created a simplified code below that creates 3 keys in a DS called “Test”, and then tries to list them with ListKeysAsync. To make sure the 3 SetAsync was successful, I first do a simple GetAsync to check the first key.
But in the loop afterwards, ListKeysAsync finds nothing: Pages:GetCurrentPage() returns an empty table:

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Test")

DataStore:SetAsync('key1', 'value1')
DataStore:SetAsync('key2', 'value2')
DataStore:SetAsync('key3', 'value3')

local Value = DataStore:GetAsync('key1')
print('Value of "key1" with GetAsync:', Value)

local DSoptions = Instance.new("DataStoreOptions")
DSoptions.AllScopes = true
DataStore = DataStoreService:GetDataStore(DataStore, "", DSoptions)

local Success, Pages = pcall(function()
	return DataStore:ListKeysAsync()
end)
if Success then
	while true do
		local Itens = Pages:GetCurrentPage()
		if next(Itens) == nil then
			warn('No records on DS')
			break
		end
		for _, Key in ipairs(Itens) do
			local Value, KeyInfo = DataStore:GetAsync(Key.KeyName)
			print('Key.KeyName, Value, KeyInfo', Key.KeyName, Value, KeyInfo)
		end
		if Pages.IsFinished then
			break
		end
	end
else
	warn('Error ListKeysAsync')
end

… will print:

image

What’s wrong?

1 Like

They are talking specifically about the excludeDeleted parameter, this does not actually exclude deleted keys

1 Like

Can I have your universeId? Feel free to PM me if its a private one. I will investigate further what’s going on.

Try this again. It should be working now and there shouldn’t be any 500 errors.
I’m not sure what’s going on with your second script but I would double check it. I’m using something similar to your first script and tested these combinations

DataStore:ListKeysAsync('global', nil, nil, true)
DataStore:ListKeysAsync()
DataStore:ListKeysAsync('', nil, nil, true)

and its working on my end. I also found no error logs for your second script so I don’t think there should be an issue with ListKeys(). Please let me know if this works :slight_smile:

You saw that I’ve added you as a contributor to this project for you to test directly, right? (I sent you a PM a few days ago).
In the 1st script, error 500 no longer occurs.
But you will see that nothing is listed, neither in the 1st nor in the 2nd script, that is, ListKeysAsync is not finding the records that were just created.
Please test it directly on the project (and if you want me to give you editing priority, you have to be a friend).

I’m sorry I can’t look at your individual script or projects. I checked your universeId and debugged the 500 errors. In terms of ListKeysAsync() it should be working normally with and without the parameters.

Here is an example of what I am running to test that ListKeys is working:

local DataStoreService = game:GetService("DataStoreService")
local DatastoreOptions = Instance.new('DataStoreOptions')
DatastoreOptions.AllScopes = true
experienceStore 	= DataStoreService:GetDataStore("TestingDatastore84187", "", DatastoreOptions)

experienceStore:SetAsync("global/Key1", 11)
experienceStore:SetAsync("global/Key2", 12)
experienceStore:SetAsync("global/Key3", 13)
experienceStore:RemoveAsync("global/Key1")

-- With defined parameters excludeDeleted true
local listSuccess, pages = pcall(function()
	return experienceStore:ListKeysAsync("global", nil, nil, true)
end)

while listSuccess do
	local items = pages:GetCurrentPage()

	for key1, info1 in pairs(items) do
		print("excludeDeleted true returns:", info1.KeyName)
	end

	if pages.IsFinished then
		break
	end
	pages:AdvanceToNextPageAsync()
end

-- With defined parameters excludeDeleted false
local listSuccess1, pages1 = pcall(function()
	return experienceStore:ListKeysAsync("global", nil , nil, false)
end)

while listSuccess1 do
	local items4 = pages1:GetCurrentPage()

	for key4, info4 in pairs(items4) do
		print("excludeDeleted false returns:", info4.KeyName)
	end

	if pages1.IsFinished then
		break
	end

	pages1:AdvanceToNextPageAsync()
end

-- With no parameters default excludeDeleted false
local listSuccess2, pages2 = pcall(function()
	return experienceStore:ListKeysAsync()
end)

while listSuccess2 do
	local items5 = pages2:GetCurrentPage()

	for key5, info5 in pairs(items5) do
		print("no params returns:", info5.KeyName)
	end

	if pages2.IsFinished then
		break
	end

	pages2:AdvanceToNextPageAsync()
end

print("finished!")

This prints
image

From a brief look at your script I recommend looking at this documentation. I suspect that you might not be using SetAsync() as intended because there is no scopes attached to the keyName.

1 Like

Yes, you can since I’m authorizing you. I’ve already done this with others on the Roblox team and it makes it easy.

The trick here is that you are forcing AllScopes = true in the DS declaration, and forcing the global scope every SetAsync, which doesn’t correspond to the reality of most cases (where you don’t declare the scope in a SetAsync).

The two scripts I gave you simulate reality, that is, I try to list ALL KEYS that have been recorded before, REGARDLESS OF THEIR SCOPE.

Here is a working version of your first script:

local DataStoreService = game:GetService("DataStoreService")
experienceStore = DataStoreService:GetDataStore("Jogo")

for i = 1, 3 do -- create 3 keys
	experienceStore:SetAsync(i, i*10)
end

function ListAllKeys(DS, Operation)
	print("\n***ListAllKeys(DS, Operation): ", DS, Operation)
	local DSoptions = Instance.new("DataStoreOptions")
	DSoptions.AllScopes = true
	local experienceStore = DataStoreService:GetDataStore(DS, "", DSoptions)

	local Success, Pages = pcall(function()
		return experienceStore:ListKeysAsync("global", nil, nil, true) -- true = excludeDeleteda
	end)
	while Success do
		local Itens = Pages:GetCurrentPage()
		for _, Key in ipairs(Itens) do
			if Operation == 'Remove' then
				local Success, Erro = pcall(function()
					local Result = experienceStore:RemoveAsync(Key.KeyName) -- remove conteúdo da chave
					print('Remove', Key.KeyName)
				end)

				if not Success then
					warn(Erro)
				end
			else
				local value, keyInfo = experienceStore:GetAsync(Key.KeyName)
				print('Key.KeyName, Value', Key.KeyName, value)
			end
		end
		if Pages.IsFinished then
			break
		end

		Pages:AdvanceToNextPageAsync()
	end
end


print("did not remove")
ListAllKeys('Jogo')
print("removed")
ListAllKeys('Jogo', 'Remove')
print("after removing")
ListAllKeys('Jogo')

You were attempting to print KeyInfo which is an object that had to be accessed.

Here when excludeDeleted = true we get this
image

when excludeDeleted=false we get this
image

2 Likes

I made some tests, and it seems to be working now.
Thank you for your effort and patience.

1 Like

@ahrielia
I opened a different bug report to this, because there are more bugs inside ListKeysAsync:

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