DataStore best practices?

The article on Data Stores seems to suggest storing all of your player data in a single store, with the user IDs as keys:

It also says:

For player data, requested keys can be unique for each player (UserId is useful for this) so it’s unlikely you’ll hit any global limit.

However, the API for GetDataStore provides example code that suggests creating individual data stores for each resource, and then using the scope parameter to differentiate between players:

Questions:

  1. Is it better to create many data stores (one per resource type per player, with keys being individual resources), or one large datastore (with keys being players)?

  2. Are the datastore limits going to be imposed differently in either of these two cases?

  3. What do “name” and “scope” actually mean in this context? If the primary key for the datastore is just (name, scope), then why not just have name? Is it to try and save some string concatenations?

1 Like

This is an important question and I’m surprised it was never answered!

Answers:

  1. It is better to use one data store so you will only have to call DataStoreService:GetDataStore() once.
  2. Limits are applied to the whole server, not a specific data store.
  3. Name is the name of the data store. Scope can be described as a folder. If you do not provide a scope, it is global by default. If you save something with the key “A” in scope 1, the key “A” in scope 2 will be nil and will not see scope 1’s data.
1 Like

One thing I now understand is that you can save a dictionary for each user. So something like:

local userData = {
["XP"] = 0;
["Cash"] = 0;
}

So, in that case, without scoping how do you do a leaderboard and order everyone in the database by XP for example? Wouldn’t that be a good reason to use different datastores? (for ordering) or is there some way you can do ordering from inside dictionary values via datastore…

Put another way. How do you show the global top 10 xp from all players if you store each players data as a dict/table?

Hmm. I guess you could just save a single object in the dataStore with a key like “global-top-10” and then store those in that dict. And just update that when a player exits.

Ah, yeah, never-mind that’s how you would do it.

Ok thanks everyone!

The article on DataStores is a bit dated. As DataStores now support 4MB worth of data in values, you’ll have very little reason to partition out with scopes. Additionally, scoping with a UserId might be muddy when it comes to GDPR compliance unless if a DataStore is rendered nonexistent if there’s no key-value pairs associated with it.

You should go with the first example that uses the UserId as a key for a single DataStore. Most games are never going to hit their 4MB limit - in fact, most games before hardly hit the 256KB limit. Games typically won’t be that data intensive, plus you have compression if it gets to that point which will shrink your characters and allow even more storage.

So questions and answers:

  1. Never make individual DataStores for resources. Always use as little DataStores as you can, where a UserId is the key and the value is all their data for the game. It makes it easier to modify or delete data as well, as all you’re doing is accessing that user key to get the rest of their data. If you need other DataStores because your resources are that large, try using the key as much as possible until you really have to change the name and/or scope. Example is class-based games with many classes and a lot of character-bound data in parallel to overall data.

  2. No. That’s why it’s dangerous to use more DataStores. Abusing them does not make them better. Consolidate where possible. Use tables as values for your keys and organise your data in the values.

  3. See: How does scope in DataStores work? - #7 by colbert2677

4 Likes