Datastore best practices

I’ve honestly have never had an issue with any sort of data saving speed, data loss, etc. I just do something like this for getting the values:

local DATA = Data:GetAsync(player.UserId.."-Leaderstats")
if DATA == nil then
	cash.Value = 0
else
	cash.Value = DATA[1]
end

And for saving I just do:

local cash = player.leaderstats.Money.Value

local DataTable = {cash}

Data:SetAsync(player.UserId.."-Leaderstats",DataTable)

I’ve never ran into an issue, I have no clue why people do all these fancy stuff, all does the same thing for me.

The big differences are that ProfileService has session locking (data will not be written until other servers have successfully saved the data), data reconciliation (where if you add new data to the data template, existing players will have that added to their data as well), service failure handling (if the datastores are down, it will attempt to manage it for you), etc.

There are a lot of great features most people wouldnt bother to add. It makes it super super unlikely to lose any data, which your players will thank you for.

:arrow_down: 1. Session locking wastes resources because the only way I can think of this is like using messagingService. Roblox instantly saves your data and I check if the data is outdated before saving anyways so if they notice that their data was lost and leave the game then rejoin it restores their data with mys ystem.

:heavy_minus_sign:2. Data reconciliation: I already have this in my code where it checks if it will override existing data using Tick()

:heavy_minus_sign:3. My script already manages this.

1 Like

If you’re looking for an alternative to ProfileService, you could also try out Suphi’s DataStore module:

This datastore implementation helps handle some of the spin-locking nature from ProfileService’s session locking, which is something you may have issues with. There’s more details about the comparison of these modules in this video:

Profile service’s session locking is at no additional resource cost, and profileservice generally has low datastore overhead.

Your script does not session lock nor does it have the useful utilities that profile service offers

The best practice is to use a DataStore module like ProfileService or Suphi’s. ProfileService is battle-tested and has been used by a number of very popular games. It also addresses all of my concerns below.

Your script doesn’t have autosaving, so if the server crashes, you’ll lose all of your data.

Your script also attempts to fetch the player’s data 5 times, but if that fails, it will just set your data to the default template. When the player leaves the game, if the UpdateAsync call works, they’ve just lost their data.

Session locking is another big issue your script doesn’t address. This is prone to servers fighting over your player data. Which opens you up to dupe vulnerabilities.

There’s also no way for another script to call your edit/save functions since you don’t use a ModuleScript or expose any API to do that.

Thank you for the feedback. Can you tell me how profile service did this and what “useful utilities” it has?

  • My script manages server crashes through its bindable event.
  • My script verifies the data before saving it with updateasync
  • How do I implement session locking? I can only think of using messaging service, which costs resources

when you use profileservice paired with replicaservice, it makes everything easier to handle

To clarify, I’m not interested in using 3rd party datastote modules on a post I’m trying to promote my own 3rd party datastore module.

How would i do session locking without uisng a service like messagingservice

(Which is part of the ROBLOX api, not third party like it may sound)

2 major issues with your module:

  1. The function connected to PlayerRemoving function won’t work properly.
game.Players.PlayerRemoving:Connect(function(plr)

	if #game.Players:GetPlayers() == 0 then
		game:BindToClose(function()
			saveData(plr)
		end)
	else
		saveData(plr)
	end

end)

According to documentation:
Fires when a player is about to leave the game

Hence, GetPlayers() will not return an empty table.

Moreover, creating a connection to BindToClose under such weird condition is incorrect.
BindToClose will trigger right after the sever is requested to be shut down.
It means, that when you decide to shutdown some server, the BindToClose will not trigger because it’s not yet initialized.
Move it out of PlayerRemoving and loop through the players saving their data.
Then, add a condition in PlayerRemoving to prevent double save.

  1. You’re not checking whether the data has been loaded before saving them.
    This is the most frequent cause of data loses so you should definitely make sure you’re handling it properly.

Generally, there’s more to improve, for example indexing directly the result of findData(). What if the data was not found?
Then, your script errors.

If you’re not experienced with creating data store systems, it’s recommended to use a battle-tested module which handles the difficulties for you.
I personally never used one but I had to learn from many mistakes. :slightly_smiling_face:

To adress your concerns:

  • if it still errors after 5 attempts, i will kick them and tell the player the game is experiencing issues

  • to adress crashes, i will add a module.save function for the developers. The developers should call this function during major events like beating a level or buying currency.

——

  • for session locking, i will add a lock boolean. Im still thinking about how i would do this without costing resources.

You do not have the correct code.

That is way outdated

This tutorial is decent and might help you improve and add more functionality such as session locking to your system:

I referenced the most recent version of the code you posted.

You have to look at the module, not the code i posted. You can find it here

Still would trigger the PlayerRemoving event which would overwrite their data. You need to check to see if the module loaded their data before updating it in the PlayerRemoving event.

If you’re sharing a public module I would leave kicking up to the developer.

This is fair. ProfileService and other modules like it usually just autosave every 30 seconds or so.

I don’t see that logic in any of the snippets you’ve posted. It simply calls saveData in the PlayerRemoving event, which is why I assumed it’s an issue.

EDIT: This reply is referencing the code snippet(s) you posted in this thread. If you want people to review your DataStore module you should post it in #help-and-feedback:code-review so people aren’t confused.

Why not just:

local Players = game:GetService("Players")
Players.PlayerRemoving:Connect(saveData)
game:BindToClose(function()
    for _, Player in ipairs(Players:GetPlayers()) do
        saveData(Player)
    end
end)

I fixed the issue regarding it saving the data even if its outdated.

I havent released it yet though since i am waiting to first add autosaving then release one big update.

That’s what I meant.
Additionally, you should make sure not to save the data twice when the server shuts down.