How can I optimize this datastore script?

How can I optimize this datastore script?

I am quite new to storing data, but I have read a lot of stuff about it, and I’m still not sure if I should change or add anything. Any help would be appreciated :)
(Also this is my first topic in this category so sorry in advance if there is something wrong :frowning: )

The code :D
local DSS = game:GetService('DataStoreService')
local playerData = DSS:GetDataStore('playerData')
local cacheModule = require(game.ServerScriptService.CacheModule)
local nosave = {}
-- configs
local errorDeb = 10
local maxStoreRetries = 30

local function retry(plr, str, errorcode)
	print('Oops! Failed to ' .. str  .. ' data for ' .. plr.Name .. ' [' .. plr.UserId .. ']!  Errorcode: ' .. errorcode)
	wait(errorDeb)
end

local function cachePlrData(plr, data)
	cacheModule[plr.UserId] = data
	cacheModule.Updated(plr, data)  -- Do event stuff for other scripts
end



local function loadData(plr)
		
	local success, errorcode
	local data = cacheModule.default
	nosave[plr.UserId] = true
	while not success  and  plr.Parent do
		success, errorcode = pcall(function()
			data = playerData:GetAsync(plr.UserId)
		end)
		
		if plr.Parent then
			if success then
				if data == nil then  -- Set to default value if no data
					data = cacheModule.default
				end
				cacheModule.Merge(plr, data)  -- Merges the loaded data if there is any already by adding up the values, so that no data is lost from either
				nosave[plr.UserId] = nil
			else
				if cacheModule[plr.UserId] ~= cacheModule.default then
					cachePlrData(plr, cacheModule.default) end
				retry('GET', plr, errorcode)
			end
		end
	end
end

local function storeData(plr)
	print(cacheModule[plr.UserId] and not nosave[plr.UserId])
	if cacheModule[plr.UserId] and not nosave[plr.UserId] then
		local success, errorcode
		local tries = 1
		local data = cacheModule[plr.UserId]
		cacheModule[plr.UserId] = nil
		
		while not success  and  tries <= maxStoreRetries do
			success, errorcode = pcall(function()
				playerData:SetAsync(plr.UserId, data)
			end)
			tries = tries + 1
			if not success then
				retry('STORE', plr, errorcode)
			end
		end
	end
end


game.Players.PlayerAdded:Connect(loadData)
game.Players.PlayerRemoving:Connect(storeData)

The code gets player data when they join, setting their current game data to the default value until it gets the actual progress. If it sets it to the default value it won’t save the default value over. It also stores the data when the player leaves. It does not store the data if it has not been loaded yet in the first place.
I hope to find some suggestions to improve it here to optimize it, since I’m new to data storing.
P.S Is the code readable enough?
Thanks in advance,
Muuun :stuck_out_tongue:

EDITED to adapt to starmaq’s solution in post 2

2 Likes

Your code seems very clean, and I like what you did with the recursion in this. Where you keep trying to save the data until it does.
But this does get expensive overtime, imagine if for some critical reason the game just can’t save the data.
It will endlessly keep on trying to save the data which might be impossible. Again, maybe I’m wrong, and even if this a thing its assumingly very rare.
Maybe something better to do is try a certain amount of time to save data and if it doesn’t meet a success after trying for the wanted amount of time, it will just give up.

local try = 0 
repeat
 --the saving
until try == 10

Other than that the script seems efficient

2 Likes

Caching datastores can be helpful, for example, you can make a DataStoreCache module like my game has like this:

local DataStoreCache = {}
local dataStoreService = game:GetService("DataStoreService")

function DataStoreCache.new(name, scope)
	local dscache = {}
	dscache._ds = dataStoreService:GetDataStore(name, scope)
	dscache._cache = {}
	return setmetatable(dscache, {__index = DataStoreCache})
end

function DataStoreCache:set(key, value)
	self._cache[key] = value
end

function DataStoreCache:get(key, value)
	if(self._cache[key] ~= nil) then
		return self._cache[key]
	else
		self._cache[key] = self._ds:GetAsync(key)
		return self._cache[key]
	end
end

function DataStoreCache:flush(key)
	self._ds:SetAsync(key, self._cache[key])
end

-- Not recommended.
function DataStoreCache:flushAll()
	for i, v in pairs(self._cache) do
		self:flush(i)
	end
end

return DataStoreCache

Here’s an example of what you can do with it (in a server script in ServerScriptService), assuming it’s in ServerScriptService.Modules:

local dataStoreCache = require(script.Parent.Modules:WaitForChild("DataStoreCache"))
local datastore = dataStoreCache.new("name_of_datastore", "scope_of_datastore")

local players = game:GetService("Players")
local datastores = {}

players.PlayerAdded:Connect(function(player)
    local save = datastore:get(player.UserId .. "-save")
    if(save ~= nil) then
        print("Save loaded!")
        datastores[player.Name] = save
    else
        print(player.Name .. " has joined for the first time!")
        datastore:set(player.UserId .. "-save", 0)
    end
end)

players.PlayerRemoving:Connect(function(player)
    local datastore = datastores[player.Name]
    datastore:flushAll() -- Save it to the datastore.
end)
1 Like