How would I get all keys in a datastore or a scope? [Datastore backup system]

So I’m currently brainstorming a design for my data system, and I found a way to back up player data, giving players the ability to restore their data from a “restore your data” menu if they needed to.

Essentially, the system would work roughly like this :

  1. A player has left the game. Save their data to their regular datastore key, such as Player_183000342 as an example. Write to a second key in the backup datastore, with the key name being <PlayerID>@<Timestamp>, where <PlayerID> is the ID of the player and <TimeStamp> is the exact time stamp that the key is being written (used as a unique identifier).
  1. If a player joins the game and clicks on the “Recover your data” menu, the game queries the backup datastore for all keys that the player owns. The data “files” are now displayed on a list UI, showing the timestamp of the “file” (not literally a file, semmantics for demonstration purposes). When the player clicks on one, and confirms via an OK dialog or something, the backup key they chose is written to their normal key in the regular datastore, essentially restoring their backed up data.

I personally love this design, but I ran into a problem - how would I find all backup keys (or a certain number of the most recent ones) that the player owns in the backup datastore (with a scope too maybe)?

I know datastore iteration was talked about at last year’s developer conference/RDC17 along with a lot of other new datastore features such as automatic retries, but there is no way to iterate through datastores at the moment.

Is there any way I could do this?


If not, what’s another way I could back up user data via unique timestamp, and let the user choose which one they want?
Could I just have 3 backup keys, named <PlayerID>Backup_1,<PlayerID>Backup_2,<PlayerID>Backup_3, and query those 3 keys?

1 Like

Short but disappointing answer, I’m pretty sure you can’t.

Could this be a potential way to do it?

You can but it’s inefficient, slow, and likely to hit limits.

1 Like

Alright.

Yeah I’m aware, I’m not exactly concerned with the efficiency of the system right now. When I start designing the actual system I’ll obviously find an efficient way of implementing it.

EDIT : I think I just found a more elegant / efficient way.

Instead of querying 3 keys, I can just query 1 backup key which has 3 tables in it, each containing the 3 backups of the players data. There’s also a number value containing the last backup number that was written to (useful for deciding when to overwrite the 1st backup).

1 Like

Hi friend

Just gonna leave this here

https://devforum.roblox.com/t/best-way-to-use-datastores-with-minimal-data-loss/32159/4

This is how I store infinite backups and can access them easily and chronologically too. Best part is I don’t hit any throttling limits (also there’s probably some people on the web team who hate me for this)

14 Likes

To our new member and non-member friends out there… (tell me if you don’t want this public)

10 Likes

Oh wow, that’s actually a good solution!

Thanks my dude.

1 Like

So how I understand it is instead of doing “player_1234” or whatever you’d put in the os.time? But if multiple people save at the same os.time wouldn’t that overwrite their data?

image

Every single player has their own OrderedDataStore

image

1 Like

I was talking about the regular datastore.

Every single player has their own regular datastore too

(this is why web hates me)

5 Likes

Okay, so I took this example from the wiki:

local DataStoreService = game:GetService("DataStoreService")
local playerExperienceStore = DataStoreService:GetDataStore("PlayerExperience")
 
local playerExperience = 0
pcall(function()
	playerExperience = playerExperienceStore:GetAsync("player_1234")
end)

“player_1234” is the key, if I were to replace it with os.time as the key, wouldn’t it overwrite the datastore if two players tried to save at the same os.time?

1 Like

I don’t think you read his post correctly.

Each player gets their own datastore, meaning there isn’t a single datastore where all player data is stored.

Would I do it like this then?

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
 
Players.PlayerAdded:Connect(function(player)
	local scope = "player_" .. player.UserId
	local lastSave = DataStoreService:GetDataStore(osTimeFromOrderedDatastore, scope)
end)

I do it like this

local DataStore = DataStoreService:GetDataStore("PlayerData"..tostring(Player.userId)) 

But yes, using scope should work too.

5 Likes

If you switched to scopes, the web team might not hate you as much. :stuck_out_tongue_winking_eye:

I think it has more to do with the terabytes of data that I’m probably hoarding and less with the organization, but yeah

3 Likes

How well does this prevent users from losing their data and how could I make data restores? I was thinking if I allowed players to revert their data to an older version, they could exploit it by trading some items, rejoining and reverting to before they traded their items, or something like that.

1 Like

Oooohhh, very very good point.

I literally did not think of that.

It looks like I won’t be allowing users to manually restore their data, but a backup system is still useful to have. I simply won’t add in the “restore your data” menu.

1 Like