Save your player data with ProfileService! (DataStore Module)

Thanks!

My framework uses a signal module with no yield support / yield catching in connected listeners for performance reasons - ProfileService is part of this framework module group and I’m trying to provide it as a public resource exactly like it is in my commercial project. ProfileService may evolve with my future projects and I’m going to continue providing it as a commercial-grade public resource.

4 Likes

Are you asking how to get the value of cash? You can just reference it normally. There are no special setter or getter functions for data in ProfileService.

1 Like

No… the title says what this module does and what it does clearly – it saves, loads, and session locks data.

That’s just some example code, don’t take it that seriously. What that function does is update the profile (aka where the data is) by adding the cash. For the client to see that change, you’d need a RemoteEvent to tell the client the cash’s new value whenever it changes

[10/01/2020] Profile:Reconcile() update!


Profile:Reconcile() is used to fill in missing string key and value pairs for Profile.Data from profile_template. Usage is shown in the updated example code:


ProfileService will now repeat the final save DataStore call indefinetly (After :Release() or game shutdown) until it succeeds - previously it only attempted to make this call once. This should further improve stability in very rare cases.


All tick() occurrences were replaced with os.clock()


Added handling for a scenario where ProfileService is run in studio without internet access - previously ProfileService would yield it’s API calls indefinetly in this rare scenario. A warning will now also be thrown.

15 Likes

So :Reconcile() will only add keys that are missing and will not overwrite existing keys based on type/value?

Will it also remove keys?

It looks like it only adds the keys. If you want to remove keys for whatever reason, I would recommend doing what @Kampfkarren did in Zombie Strike and use migration/revision modules for each major change in your data structure.

So, after reading through the docs and looking at the provided example, I’m already sold! I’m definitely planning to use this in my game, rather than the alternatives. I understand the concept of how to use ProfileService in conjunction with player data, but I’m a little confused on how I might use it for shared server data, that’s not tied to a specific player.

Take this hypothetical scenario:
My game has a shop which contains Roblox catalogue items. Each item is stored in a table, containing an AssetId, Name and Price. At any time, I should be able to add, update or remove these items. The server will be able to dynamically update the catalogue list without requiring a shutdown in order for all players to then see the updated catalogue; rather, it’ll just fire off a RemoteEvent with the updated data.

The structure of each item might look like this:

{
	Name = "Beautiful Hair for Beautiful People",
	AssetId = 16630147,
	Price = 100,
}
  1. What would be the best way to go about storing this data? One item per Profile or in a table in a single Profile? One item per Profile would be silly, as I’d need to know exactly which items are saved to know which key to enter. This would also take up many precious DataStore requests, as there could potentially be hundreds of items, and so requests would begin to fail due to rate-limting. Logically, you’d have a single Profile holding all catalogue items’ data.
  2. How would I have each server sync up to the catalogue, so that if I add, remove or update an item, all servers (eventually) receive the updated catalogue? Presumably this would use the Global Updates mechanism, but how would this work when all servers should be listening for updates at once?
  3. Is it possible to allow any server at any given time to create, update or delete items from the catalogue? Presumably the server will need to “steal” the session to do this?

This is only hypothetical, but I feel this could be a legitimate scenario (or something similar; e.g. promo codes), and would be very useful information to have, either explained or in the form of a tutorial or example.

1 Like

ProfileService is a specialised module for accessing data in one server at a time (and preventing collisions when players are hopping servers). You’ll have to use regular datastores for your problem.

Note that Roblox is now discouraging use of datastores for globally accessed data in favor of MessagingService

3 Likes

Thanks for the reply, and that’s the answer I expected really. Nevertheless, I really like the idea of this module, and am seriously considering using it in my game.

3 Likes

I’ve been somewhat iffy about steering away from other DataStore implementations that aren’t DataStore2, but I think I’m sold. The biggest gripe I’ve had about DataStore2 is that I can’t use Merely’s SoftShutdownService, which this implementation is very good at. Most have struggled to steer me away from it due to the fact that they usually have “warnings”, but loleris being the great salesman he is, definitely convinced me otherwise. The documentation explains the myths and misconceptions about a system like this perfectly. I’m definitely going to migrate my DataStores to ProfileService, and you should too.

2 Likes

Hey, this is absolutely fantastic! I have been using it for a while. Great work.

I have 2 questions though

  1. Is there any way to wipe data besides changing the key?
  2. How many values can I store in the ProfileTemplate array before it’s too much?

Thanks for the help.

When there are datastore outages (like today) and you go to retrieve the profile. Will it be returned as nil?

1 Like

I don’t know about live games, but during the outages, it gave me HTTP 500 warnings (IIRC) from ProfileService in Studio. I assume it then kept attempting to load, since after I waited for awhile, it loaded my data.

In conclusion: ProfileService is reliable.

2 Likes

The only time you should have issues is if Roblox accidentally gives you nil when there’s valid data stored somewhere. Many Roblox games experienced this issue a couple of years back for a day or so. That kind of thing is out of the control of anyone.

1 Like

It will yield indefinetly until a profile is loaded. nil is returned for other kinds of rare exceptions after which you should kick the player.

3 Likes

How do you use WipeProfileAsync?

So previously I wrote an interface to serialize/deserialize my data to Folders and ValueBases, and to update their keys in their respective tables in Profile.Data as the ValueBases are updated. Now I realize instead of trying to update Profile.Data as my ValueBases change, I can just serialize everything once when their profile is released like so:

profile:ListenToRelease(function()
	local data = leaderstatsToTable(player.PlayerData)
	profile.Data = data
	profileCache[player] = nil
	player:Kick()
end)

So my question is, serialization is a valid use of ListenToRelease, right? The first time I read this documentation:

After Profile:ListenToRelease() is triggered, it is too late to change Profile.Data for the final time. As long as the profile is active ( Profile:IsActive() == true ), you should store all profile related data immediately after it becomes available. An item trading operation between two profiles must happen without any yielding after it is confirmed that both profiles are active.

I was confused on whether I was allowed to modify Profile.Data in ListenToRelease.

Also, in the first line of ListenToRelease I printed Profile:IsActive() and it printed false. So, as per the IsActive documentation:

Returns true while the profile is session-locked and saving of changes to Profile.Data is guaranteed.

This worries me further that I’m doing something wrong. Am I allowed to serialize data in ListenToRelease?

2 Likes

You’re not allowed to - profile release can happen unexpectedly, issued by another server - that’s why :ListenToRelease can’t be used for final saving.

ProfileService forces you to keep your data saved in a form that, in case the server crashes or some other problem occurs, will protect player’s data with minimal data loss.

5 Likes

Presumably there are no issues with ProfileService in the event of an outage like DataStore2 experienced, where data became out of sync due to OrderDataStores not being affected by the recent issue?

When Roblox performs a rollback, is player data just reverted to whatever version they restored to?

https://devforum.roblox.com/t/datastores-incident-report/829962/1

Yup. ProfileService uses one key per profile, so it’s going to be compatible with Roblox’s own versioning systems.

2 Likes