Datastore code review for inventory, how can i reduce data queues?

it works fine with 1 - 2 players but 3 or more when joining or leaving causes data queue which causes data loss and duplication of other inventories to other players, but that happens not much.

--Services--
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")

--Shortcuts--
local Folder = ReplicatedStorage:WaitForChild("DataStore")

--Variables--


--functions--
local function GetDS(Value, Player)
	local DS = DataStoreService:GetDataStore(Value.Name)
	local Data;
		
	local DataFetchSuccess, ErrorMessage = pcall(function()
		Data = DS:GetAsync(tostring(Player.UserId))
	end)
		
	if DataFetchSuccess then
		if Data == nil and Value.Name == "DeafultKnife" or Value.Name == "DeafultFlashlight" or Value.Name == "Clown" then
			Value.Value = true
		elseif Data == nil and Value.Name == "Skin" then
			Value.Value = "Clown"
		elseif Data == nil and Value.ClassName == "StringValue" then
			Value.Value = ("Deafult"..Value.Name)
		elseif Data == nil and Value.ClassName == "BoolValue" then
			Value.Value = false
		elseif Data == nil and Value.ClassName == "NumberValue" then
			Value.Value = 0
		else
			Value.Value = Data
		end
	end
end

local function SetDS(Value, Player)
	if Player.CanSaveData.Value == false then return end
	local DS = DataStoreService:GetDataStore(Value.Name)
	local Data = DS:GetAsync(tostring(Player.UserId))
	
	if Data ~= Value.Value then
		local DataWriteSuccess, ErrorMessage = pcall(function()
			DS:SetAsync(tostring(Player.UserId),Value.Value)
		end)
		
		if not DataWriteSuccess then
			local Retry = 0
		
			while Retry < 6 do
				wait(60)
				local Succeded, Error = pcall(function()
					DS:SetAsync(tostring(Player.UserId),Value.Value)	
				end)
				if Succeded then break end
				Retry = Retry + 1
			end
		end
	end
end

local function CheckChildren(Key, Children, Player)
	for i = 1, #Children do
		if Children[i].ClassName == "Folder" then
			local C = Children[i]:GetChildren()
			if C then
				CheckChildren(Key, C, Player)
			end
		else
			if Key == "GetDS" then
				GetDS(Children[i], Player)
			else
				SetDS(Children[i], Player)
			end
		end
	end
end

game.Players.PlayerAdded:Connect(function(Player)
	
	--//Loading DataStores\\--
	local Clone = Folder:Clone()
	Clone.Parent = Player
	local FolderChildren = Clone:GetChildren()
	CheckChildren("GetDS", FolderChildren, Player)
	
	-------------------
	print("DataStore Loaded")
	--CanSaveValue--
	local CanSave = Instance.new("BoolValue",Player)
	CanSave.Name = "CanSaveData"
	CanSave.Value = true
	
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local Clone = Player:FindFirstChild("DataStore")
	if Clone then
		local CloneChildren = Clone:GetChildren()
		CheckChildren("SetDS", CloneChildren, Player)
		print("Inventory Saved")
	end
end)

I think you should cache your data rather than setting it every single time it needs to be updated. This means you would have a variable with the data, and only set the datastore to that variable every minute and when the player leaves/game closes. And whenever you need to update the inventory, you just update the variable.

8 Likes

I dont quit understand. The code only sets datastore everytime a player leaves. And when the player is connecter its all dependant on in game server values. I dont know why it would cause a queue.

When the player is leaving, they are setting every single item individually it looks like. So this means they are using :SetAsync() like 10 times or more depending on how many items.

Sorry if Im am being an idiot right now but Im pretty sure the data store is only called when a player joins and when he leaves.

If you’re having issues with queues and DataStore calls being held back, then that’s obviously an issue with the structuring of your code and how many calls you’re making. Realistically, you shouldn’t be making many calls in a player DataStore scenario; only

  • When the player enters
  • When the player leaves
  • When the server shuts down
  • During game time if you realistically need to fetch or update player data

So obviously, this means that what you need to look into is a session cache system that holds data and updates it while the player is active in-game and removes it once it’s no longer necessary.

Honestly, I don’t have much to say for this Code Review other than that you should probably rewrite your code. The fundamental flaw you’ve made is treating every key of data for a player as a separate DataStore, which is a fatal-yet-common error.

As for SetDS, I noticed that you use GetAsync, for some reason. In this case, it looks like you’re using it as a validation condition to prevent using SetAsync if the value is the same. Don’t. This is where you should be using the underappreciated UpdateAsync. It does use a get and a set request but the plus points for this are that

  • It performs a non-caching Get request. GetAsync caches return values for 4 seconds and can provide you with an inaccurate value and

  • The old data is passed into the function. You can simply check if the new data is not the same as the old data and then pass your new data, otherwise return nil (cancels the write request).

tl;dr
Rewrite your DataStore code. Treat player data sets as tables and save with tables, not individually.

1 Like

I made a caching method like what you would want here: How can I optimize this datastore script?