Save your player data with ProfileService! (DataStore Module)

I’ve noticed, when viewing multiple profiles at once

ProfileStore:ViewProfileAsync(`Player_{UserId}`)

It kinda like “overloads” profileservice, if multiple calls to viewprofile is happening
loadprofile doesnt work anymore…?

After all the ViewProfiles have finished data loads again like normal?

you mentioned a “remote server.” What do you mean by this?

You also specified that it waits an entire minute which is quite a long time for a user to get their data.

Why not just kick the player if a session already exists or if you have this magical “remote server”, kick all the players on the previous servers and load the new one?

ProfileService waits a minute for the other server to release the session lock on the players data so that two servers can’t edit the players data at the same time. If the server that has a session lock on the players data does not respond in time, ProfileService forceloads the data.

The remote server is the server that has a session lock on the players data I assume.

1 Like

why not wait like 10 seconds instead? A full minute is quite excessive considering the situation is likely a player either crashed or the getter beat the race condition from the setter. 5-10 seconds would be a more manageable timeframe to retrieve data rather than a full minute. Also having to do frequent checks within this minute could be a of requests.

@loleris

Well, I am clearly not loleris so I can not answer that question on his behalf. I’m just explaining what I know about ProfileService.

its possible the reason it waits a full minute is because the “remote server” is really just the autosave script.

For example, every time it autosave, it retrieves the current information about the session lock. When a different server changes the current session lock, the autosave loop will catch onto it and cancel its current session, allowing the new session to be the most recent.

Is there a way to have saveable and temporary data in the same table? I saw a response by loloris and have attempted to implement it but my understanding of profile service is limited so I am unsure if this is the correct way to achieve this.

if player:IsDescendantOf(Players) == true then
			local player_data = {Save = profile.Data, Session = {}}
			
			Profiles[player] = player_data
			
			-- func

Ideally anything under " Session " would not save, while everything under profile.data would.

Any proof to support that claim?

Its a claim on subjective basis not objective facts. While I could pull up studies of “9-10 surveyers voted that they would rather wait 10 seconds for data to load than a full minute” but I dont think such studies exist. It is simply a matter of logically reasoning the probable desires of players

I use profile service it is so nice, is there anything wrong with it? I never ran into any issues.

Its well made and tested but has a bit of a learning curve and over complicated the data saving process. A lot of the stuff you have to set up manually and manage (such as having to disconnect and connect players when they join/leave) should be automatixally done to make using the module easier.

This was the main reason I created EasyDatastore, which is like profile service however you dont have to setup any kind of profiles or configurations since its done for you. It has only 2 functions: get() and set(), which is all you need to manipulate and save data.

Now the reason I am on this post is not because I am here to talk about EasyDatastore, but because I want to talk about profileservice. To my understandint, profileservice forces the player to wair a full minute when a session is locked for this “remote sever” (presumably the autosave function.) i think this time is quite excessive (although I just kick the player)

connecting and disconnecting players are literally just a couple of lines

Oh thats okay, its recommended by so many people and has well over 100 guides on how to setup and configure a few things. I also follow your EasyDatastore, and it is nice and I recommend it for new developers, I think I even posted on that. PS is very much for a certain set of people. I don’t think EasyDS can achieve the same things PS can though. I remember talking about tables inside tables and other more complex things, but you might have added support for that IDK.

To clarify I am talkkng about EasyDatastore V2, not EasyDatastore V1. EasyDatastore V2 is just as reliable and supportive as profile service, being designed for even large scale games and projects. It supports all datatypes and is setup by creating a data template withon the EasyDatastore module script.

Its still redundant to do so. Even if its “just a few lines” it adds complexity to the module and is overall extra. It makes the learning curve more difficult for no reason too.

If ProfileService does something, it does so for a good reason. Also ProfileService waiting for over a minute until another server releases the profile is a case, not the regular behaviour of ProfileService where any profile will usually load as fast as a single DataStore call would take - you need to mess your implementation of ProfileService up to run into that case. Even so, thanks to how ProfileService handles cross-server conflicts, it can still retain a reliable :IsActive() state that guarantees “atomicy” between two loaded profiles on the same server even if you mess a lot of things up.

During a session lock conflict where ProfileService would need to wait for over a minute it lets the server that currently has the profile loaded that it has to release the profile and switch off the :IsActive() state. If the new server grabbing the profile would take ownership more hastily then this guarantee would no longer exist.

If your game doesn’t have trading or item storage between multiple data store keys - you probably won’t run into any problems with any method for storing into the DataStore. However if you have these features in your game - not using ProfileService or some kind of other trick to prevent a server hop server state mismatch you’ll be in for some surprises down the line.

5 Likes

Can you clearly outline how session locking works specifically for profile service. Here are some questions I have about the current system.

  1. What is this “remote server” you have talked about in the past? Do you mean the autosave script? If it’s not, what and how have you implemented this? Specifically what services do you use?

  2. How do you check quickly whether the new server takes ownership? Doesn’t it require frequent requests for the data to check if it has been claimed?

  3. Arent the only situations of session locking being server crashes (the game can’t unlock the session on leave) and the getter beating the race condition of the setter? (perhaps in the case of PS, it would be poor implementation as PS doesn’t automatically manage this)

Considering the only purpose of session locking is to prevent the getter from beating the setter, couldn’t simply waiting a few seconds (if the session is inaccurate) suffice to allow the setter to fully process? (considering that the developer implemented data saving correctly on not just PS but their own management of datastore service too?)

It seems like all these robust aspects of PS is simply to cover for user error, but not needed for correct implementation.

I’m not gonna go into detail on how session locking works because it has been covered in this thread several times already by me and other people - there are people who have made explanations on how session locking works on Youtube.

And yes, you’re right - waiting a few seconds before you load player data is one of the ways you can somewhat prevent dupe exploits, but without a system that keeps track on whether a profile is currently in use, you’re going to blindly wait for anywhere from 5 seconds (Which might not be enough) to 60 (Which should be plenty, but good luck making players wait that long). ProfileService only waits when it’s necessary and majority of the time won’t make you wait longer than a single UpdateAsync request would, BEATING a system that makes you wait.

ProfileService was intended to be a solution to dupe prevention for developers who make games that involve trading. Any secondary thing ProfileService solves is way less important. The fact that ProfileSerivce even exists is the clear lack of Roblox native support for such data security measures they’ve been postponing for way too long - data in games changes rapidly, there are needs to have trading and other interactions between players and the people coding those features are not geniuses and need simple solutions.

1 Like

Having it done automatically is good, but… it’s not all of the time, especially when Roblox servers are struggling with datastores. If the player gets teleporting, the server sees it as if they left. Datastore2, which automatically detects this, will save their data. But imagine LOADING data was much faster than SAVING it. The new server would load old data since it wasn’t saved yet.

With ProfileService, you can tell it to save BEFORE they teleport. You can then ensure it’s saved by using ListenToHopReady, which calls the function you give it when they are ready to teleport.

The reason I chose Datastore2 at first was because I saw ProfileService as having a big learning curve, and that it was overcomplicated. But once you get over that small hurdle, it’s actually a lot better than Datastore2.

I am aware of the basics of session locking, which I have implemented in my own datastore module.

How it currently works is that session locking is managed by having a table with all the sessions and the start time of their dates.

Then, when they try to open a new session, it will add the session to the table and attempt to wait until the other session is cancled, waiting every 5 seconds.

On the older session, if the save systems see the new session inside the table, it will save the data as well as remove its current session. If possible it will also kick the player in this session.

If it takes no more than 30 seconds, the older sessions will automatically be abandonded (as the server perhaps crashed)