DataStore SetAsync only working sometimes, help?

Hello! i’m trying to work with datastores right now and ive stumbled into a real massive issue, my datastore only seems to actually save things at random, sometimes it saves, sometimes it doesnt do anything, it seems that it’s mostly the second one, i littered my code with prints to be able to diagnose what worked and what didn’t, and i managed to find that it’s the SetAsync that’s sometimes not working

There’s a few issues with this, though, the SetAsync is on a pcall, which is supposed to print error messages if it works or not, and nothing prints, so it seems like the setasync just completely pauses the script, here are some pictures of the output
imagen_2023-02-12_011849996
Here’s the output when it actually worked, the only time it properly worked, i know, creative prints, it’s whatever.
imagen_2023-02-12_012014747
Here’s the output when it doesn’t work, aka, most of the time, you can see that it cuts off at the “WILL DO” print, which is right before the SetAsync

Here’s a code sample of the save function

local function saveData(player) -- The functions that saves data
	
	local tableToSave = {}
	
	tableToSave = tableFromFolder(player.CarData)
	print(tableToSave)
	
	print("WAITED")

	local success, err = pcall(function()
		print("WILL DO")
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
		print("DID")
	end)
	print("AAAHHH")
	if success then
		print("Data has been saved!")
	else
		print("Data hasn't been saved!")
		warn(err)		
	end
end

any help??? please? i am super lost with this, i’m willing to provide more code samples on request, as i need as much help as i possibly can, many many thanks!!

2 Likes

you should instead have a system where you repeat until the pcall was a success or if the Data has reached a limit:

local Attempts = 1 -- Attempt Counter
local Success, Error -- do not mess with these, this is so we can access the pcall within the loop
repeat
    Success, Error = pcall(dataStore.SetAsync, dataStore, player.UserId, tableToSave) -- this is apparently the "better" way
    Attempts += 1 -- Adds Attempt
    if not Success then task.wait(2) end -- if failed, waits to try again (firing very fast can cause a queue)
until Success or Attempts > 3 -- Conditions to Break upon

if success then
    print("Saved Data!")
else
    warn("Failed to Save Data: '" .. err .. "'")	
end

You should also try using UpdateAsync instead of SetAsync

SetAsync is never going to work all the time, Data Loss happens, and there are many ways to prevent it, sometimes the code doesn’t even fire if you are leaving the game.

2 Likes

Sorry, what you mean about that? That SetAsync doesnt even trigger when player is leaving the game?

@ArisNeta when are you triggering that saveData function?

1 Like

When leaving the game (if they are using it when the Player is Leaving), The Connection wont fire. (It would, but somethings it wont, if you understand what i mean) which is generally why people use BindToClose for this

However, I’m kinda straying.

1 Like

Sorry, I never experienced that issue before. If a SetAsync inside a pcall is called on PlayerRemove event, it doesnt fire all the times you mean?

Yup, thats what I was about to say, that the last player in a roblox server leaves or when working on studio if you close it, the way to prevent the data loss would be using BindToClose

1 Like

Yes.

1 Like

Yup I agree, but its not a problem about using SetAsync, its a logical issue when the server shutdown obviously it wont be able to perform any task

1 Like

its being triggered on two possible occasions, on player leaving (playerremoving), and on server closing (bindtoclose)

I’ll try the script changes when i wake up, it’s 3 am and i decided to take a break from working because this thing was stressing me a lot (thanks @DasKairo btw)

1 Like

i’m assuming the server is shutting down before the data saves when the last player leaves the game.

try this:

1 Like

What I did to counter this was to make the game save in BindToClose and not stop until it reads the data to make sure it saved.

1 Like

saveData() is being called after the game is being closed aka via leaving. Can you show full code?

1 Like

I tried setting it up like this

local function saveData(player) -- The functions that saves data
	
	local tableToSave = {}
	
	tableToSave = tableFromFolder(player.CarData)
	print(tableToSave)
	
	print("WAITED")
	
	repeat
		print("WILL DO")
		Success, Error = pcall(dataStore.SetAsync, dataStore, player.UserId, tableToSave) -- this is apparently the "better" way
		Attempts += 1 -- Adds Attempt
		print("DID")
		if not Success then task.wait(2) end -- if failed, waits to try again (firing very fast can cause a queue)
	until Success or Attempts > 3 -- Conditions to Break upon
	
	print("AAAHHH")
	if Success then
		print("Data has been saved!")
	else
		print("Data hasn't been saved!")
		warn(Error)		
	end
end

And this was the result:
imagen_2023-02-12_141221298
Doesn’t seem like it exactly worked, unfortunately

(i defined attempts and success,error earlier at the start of the script, by the way)

Sure, here’s the full code, it’s from a datastore tutorial that i saw on the devforum a month ago, just, very sketchily modified to work with my game’s requirements

local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("PlayerStatsStore")

-- GUIDE TO THE ORDER OF THE DATASTORE--
-- there is no guide

function tableFromFolder(folder: Folder) -- If you want ANY instance, replace with "Instance"
	local folderTable = {}
	-- Loop through each child
	for _, child in folder:GetChildren() do
		-- If it is a folder, use recursion to do the same and
		-- convert its children into another table and use that
		if child:IsA("Folder") then
			folderTable[child.Name] = tableFromFolder(child)
			-- If it is a value (such as StringValue or BoolValue), store its value directly
		elseif child:IsA("ValueBase") then
			folderTable[child.Name] = child.Value
		end
	end
	-- Return the finished result
	--print(folderTable)
	return folderTable
end

local function saveData(player) -- The functions that saves data
	
	local tableToSave = {}
	
	tableToSave = tableFromFolder(player.CarData)
	print(tableToSave)
	
	print("WAITED")

	local success, err = pcall(function()
		print("WILL DO")
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
		print("DID")
	end)
	print("AAAHHH")
	if success then
		print("Data has been saved!")
	else
		print("Data hasn't been saved!")
		warn(err)		
	end
end


game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves the game
	saveData(player) -- Save the data
end)


game:BindToClose(function() -- When the server shuts down
	for _, player in pairs(game.Players:GetPlayers()) do
		saveData(player) -- Save the data
	end
end)


game.Players.PlayerAdded:Connect(function(player)

	local DataFol = Instance.new("Folder")
	DataFol.Name = "CarData"
	DataFol.Parent = player
	
	local children = game.ReplicatedStorage.Cars:GetChildren()
	
	for i, child in ipairs(children) do
		if child:IsA("Model") and child:FindFirstChild("CarColor") then
			local Fol = Instance.new("Folder")
			Fol.Name = child.Name
			Fol.Parent = DataFol
			
			local Dist = Instance.new("NumberValue")
			Dist.Name = "Distance"
			Dist.Parent = Fol
			
			local ServDist = Instance.new("NumberValue")
			ServDist.Name = "ServDistance"
			ServDist.Parent = Fol
			
			local Tire = Instance.new("StringValue")
			Tire.Name = "Tire"
			Tire.Parent = Fol
		end
	end

	local data
	local success, err = pcall(function()

		data = dataStore:GetAsync(player.UserId)

	end)

	if success and data then
		
		print("DATA")
		print(data)
		
		local children = player.CarData:GetChildren()
		
		for i, child in ipairs(children) do
			if child:IsA("Folder") then
				print("found child: "..child.Name)
				local cardata = data[child.Name]
				print("CARDATA")
				print(cardata)
				
				if cardata ~= nil then
					print("found data!")
					child.Distance.Value = cardata["Distance"]
					child.ServDistance.Value = cardata["ServDistance"]
					child.Tire.Value = cardata["Tire"]
				end
			end
		end
	else
		print("The player has no data!") -- The default will be set to 0	
	end

end)

UPDATE:

@DasKairo’s fix worked, but there’s one tiny issue, it won’t print, which is fine, as long as it works, and that’s great :slight_smile: thank you all so much!!! i’m glad that i can finally get this stress of my back

UPDATE UPDATE:

It seems like it doesn’t actually 100% work, it just makes it like, more possible that it will work, its super strange, and still won’t print :frowning:

edit: i set the amount limit to math.huge, is this a terrible idea? yeah

is the data very sensitive? no not really

…edit 2:

even with the math.huge limit, it still sometimes doesnt work …

What code do you have in BindToClose?

In post #13, i posted a full sample of the code (before i applied the code sample provided in post #2), bindtoclose just indexes players then fires the saveData() function for all players it finds

Ah, got it. You need to use DataStore.GetAsync to verify that the data has been written before closing the game.

How would i use it though? sorry, i’m not very used to data saving yet

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