Why is my data not always saving?

I’ve been using a leader stats script that I’ve made myself, but I’m currently having issues with the data saving. For some reason, my datastore’s decide if they want to save the data or not depending on how there feeling. Because my data has barely been saving recently, and I don’t know why…

There are no error’s, but here’s the script.

--\\ Datastores - Made By StarJ3M //--
local DataStoreService = game:GetService("DataStoreService") -- Datastore Service

local totalBricksData = DataStoreService:GetDataStore("totalBricks") -- The All-Time Bricks
local studData = DataStoreService:GetDataStore("studData") -- The Currency
local killSoundData = DataStoreService:GetDataStore("savedKillSound") -- The Kill Sound for the player

local playerItemData = DataStoreService:GetDataStore("playerItemData")

local shopItemData = DataStoreService:GetDataStore("shopItemData")

local shopOptionsRemote = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents"):WaitForChild("LoadShopOptions")
local shopItemCancelRemote = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents"):WaitForChild("shopItemCancelRemote")
local shopItemPromptRemote = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents"):WaitForChild("shopItemPrompt")
local shopModule = require(game:GetService("ReplicatedStorage"):WaitForChild("ShopModule"))

--\\ Functions //--

function AddComma(Data)
	Data = tostring(Data)
	return (((Data:reverse()):gsub("%d%d%d", "%1,")):reverse()):gsub("^,", "")
end

game.Players.PlayerAdded:Connect(function(player)
	local killSound = Instance.new("Sound", player)
	killSound.Name = "killSound"
	killSound.Volume = 2
	killSound.SoundId = game:GetService("ReplicatedStorage"):WaitForChild("KillSounds"):WaitForChild("default").SoundId

	local items = shopModule.createOwnedData(player)

	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"

	local Studs = Instance.new("NumberValue", leaderstats) -- The Currency
	Studs.Name = "Studs"
	Studs.Value = 0

	local Bricks = Instance.new("IntValue", leaderstats) -- The Bricks broken between matches
	Bricks.Name = "Bricks"
	Bricks.Value = 0

	local totalBricks = Instance.new("IntValue", leaderstats) -- All time bricks broken
	totalBricks.Name = "T. Bricks"
	totalBricks.Value = 0

	local brickData
	local studsData

	local itemsData
	local possibleKillSound

	local success, errorMessage = pcall(function()
		brickData = totalBricksData:GetAsync(player.UserId)
		studsData = studData:GetAsync(player.UserId)

		possibleKillSound = killSoundData:GetAsync(player.UserId)
		itemsData = playerItemData:GetAsync(player.UserId)
	end)

	if success then
		totalBricks.Value = brickData
		Studs.Value = studsData
		killSound.SoundId = possibleKillSound

		items = itemsData
	elseif errorMessage then
		warn(player.Name.."'s data cannot be found!..")
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local leaderstats = player.leaderstats

	totalBricksData:SetAsync(player.UserId, leaderstats["T. Bricks"].Value)
	studData:SetAsync(player.UserId, leaderstats.Studs.Value)
	killSoundData:SetAsync(player.UserId, player.killSound.SoundId)

	local playerItemTable

	for i, v in pairs(shopModule.listOfPlayerItemTables) do
		if v.ownerTag == player.Name then
			playerItemTable = v
		end
	end

	playerItemData:SetAsync(player.UserId, playerItemTable)
end)
4 Likes

Don’t forget to use BindToClose it is really important when it comes to data saving.

2 Likes

:joy:

As @AustnBlox stated, use the BindToClose, you are saving many Datastores when Studio is closing, is not having enough time to save everything.

Dont forget to wrap GetAsync and SetAsync on pcalls(), otherwise you wont be able to handle when Datastore saving or reading failed, certain APIs tends to fail due to connection issues, etc. You need a safe way to handle it by using pcall()

3 Likes

Ok wow. I cried a little bit when I saw this. That’s A LOT of data stores… There’s a limit to :SetAsync() and :GetAsync()

Set Async(s, Because there is different types) share a limit: 60 + numPlayers × 10
Get Async Has the same limit: 60 + numPlayers × 10

This is important because if you shut down a server with like 10 players (idk how big your game is), 2 issues can arise:

  • Server doesn’t have time to go through all SetAsyncs() requests
  • You get limited and cannot save all the data.

I highly recommend using a table then 5 data stores if possible like this:

local TableToSave = {
["studData"] = 0,
["totalBricks"] = 0,
["savedKillSound"] = "",
["playerItemData"] = {},
["shopItemData"] = {},
}

This way you would only use 1 datastore, instead of 5. Which would help prevent data loss as a whole.

This is my recommendation.

4 Likes

I agree totally with @OfficialPogCat, you should try to compress all possible data into only one DS. Sometimes you will need OrderedDatastores, which mandatory you will need to create a new one DS.

Hey @OfficialPogCat, actually I worked in a game that had around 6 datastores, servers of 25 players, and I never saw a datastore queuing due to many calls, or players losing its data… Im still struggling to understand the limits of DSS. Firstly I thought that its per DS, then I noticed the queuing warning was based per keys.
Plus, when a server shutdown has 30 seconds to finish the BindToClose functions.
And when dealing with releasing updates and shutdown servers I prefer to send a notification on player’s screen game is gonna be updated on X minutes, and the system will kick all players in all servers making sure its data is saved correctly.

Mmmh, still a little confusing to me, how the DSS limits exactly works…

4 Likes

I used the roblox documentation for this. I highly recommend checking it (though I am sure you have) as this was from roblox: https://create.roblox.com/docs/scripting/data/data-stores#limits. From reading it seems like a server wide limit, not each owns data store limit “Each server is allowed a certain number of data store requests based on the request type”

Intresting! To be fair you are only using half (ish) of the limit (310 allowed requests a minute, 150 used). For me I don’t like that as 30 seconds for 150 requests, with each request taking 6 seconds, are likely to have errors, or atleast, is a high risk of having them.

The real problem might be people that leave and rejoin though. If people often leave and rejoin you might reach that limit way too often because of that, but it would have to be many leave and rejoins, within the minute of a shutdown.

But again, would rather not take this risk when we can, which in this post looks like we can make it into one datastore

3 Likes

But, each request will not take 6 seconds. That cooldown is when rewritting the same key. If you save the key UserID1564647896 once, you should wait 6 seconds cooldown to rewrite that same key.

If the 25 players leaves the game at the same time, indeed are 150 request when having 6 datastores, but each SetAsync is independant to each other, so its not waiting 6 seconds to save the next one.

And yup, 150 is the half of the 310 request allowed per minute. So thats why theres no issues. And 30 seconds seems like enough time to do those 150 request, cause the cooldown is when rewritting the same key, not when saving on independant DSs

Yup. Are there is ways to handle that. Still sending 150 requests in 6 seconds sounds like a lot, and even with big games, Like pet simulator x, if they use 6 datastores, 30k players after an update shutdown, would be 180,000 requests within those 6 seconds. And I feel like I wouldn’t trust roblox enough to make sure each 180,000 requests are working in my experience.

But yes, I understand the cooldown isn’t per each request, but rather the time to save that data to a key in a datastore, and they are independant

2 Likes

Its a topic that after a couple of years here in Roblox I still dont understand it quite right haha…

Why in 6 seconds? Server would have 30 seconds to deal with the 150 requests on shutdown.

I dont think those being accumulative, are per server, if there is 25 players per server then 310 request per minute allowed. Having 6 datastores 150 request to be handled on 30 seconds, and not having a cooldown per saving request

1 Like

Fair point. It’s late at night ok haha

And I will never understand a clear answer. Just assume somethings and adding logic

2 Likes

Why can’t I just use regular data stores with this?

What’s the difference from this and ordered data stores?

1 Like

You can try using the module by @Kampfkarren :

It was made to prevent data loss, and it’s relatively simple to use.

1 Like

Won’t this mean I’ll have to re-write my entire script?

1 Like

No, you won’t have to do that. You can just put the values in a table and save them using the module.

1 Like

Something like this:
local data = {
variable1 = value1,
variable2 = value2,
variable3 = value3,
etc…
}

1 Like

Something like this:
local data = {
variable1 = value1,
variable2 = value2,
variable3 = value3,
etc…
}

edit: and just use the module functions :Set and :Get

Can I still just use the basic Roblox Datastores? I was told I should use BindToClose() earlier.

1 Like

I personally believe DataStore2 is simpler to use. As I don’t have as much experience using normal datastores, I cannot give you a solid answer regarding the :BindToClose.

1 Like

You can use other datastores systems, and each system is different and each system might not require BindToClose()

However if you are looking at making a datastore using the default roblox method, BindToClose() should be used, as well as player removing. Just means that you have to handle everything yourself, instead of using another source like DataStrore2 / Profile service etc.

I am pretty sure @Dev_Peashie is saying that You should only make more then one data store, if you are using an OrderedDataStore. Not saying you should make one with the current data. OrderedDataStores is used to sort data, like with a global leaderboard system. From Roblox Documentation :

“By default, data stores do not sort their content, but sometimes it’s necessary to get data in an ordered fashion, such as persistent leaderboard stats” .

So Use a Normal Datastore, and Only 1 Datastore for your case, unless you are needing an OrderedDataStore

3 Likes

Have you also tried using a pcall() function to check for errors while saving playerdata? Additionally, if playerdata is saving too quickly, it will start to queue. During the queue, if a player leaves while their data is in queue, it will not save. You can try and prevent this by saving data more, or only when it changes.

1 Like