I am currently facing an issue where ProfileService is taking way to long to load players profiles, this is causing many players to complain about long loading times. We have servers with about 60 players, and I’ve noticed with the more players there are, the slower the times are for loading the players profile. Whenever they’re in a server by themselves or even 20 people, it loads within seconds, but when theres about 60ish, it takes multiple minutes.
Is there any way to a solution to this? I’ve looked at many threads about this same problem but none of them have found an official solution.
This is a major issue as it’s bringing people’s attention about the game down as they don’t want to have wait mere minutes just to load into the game and have fun, I would hate to have to change to a new DataStore especially after spending weeks setting everything up and making it work perfectly, just sadly the profiles take way to long to load which is a bummer.
What method are you using to load player data? Repeat, Cancel, ForceLoad or Steal?
Are you sure the .PlayerRemoving event is releasing your profile successfully? If not then it may be an issue when the player rejoins as the profile will stay active in that server.
It’s going to be pretty hard for anyone to provide assistance without seeing examples of how you’re using ProfileService. Could you post some snippets showing how you’re interacting with the module?
Which looking at these errors, it’s only happening to certain people and not everyone, which doesn’t really make sense, you can see one each one, it’s a different ID but it shows that it’s 1.8k each time. Unless it’s stacking ALL of them together and just displaying that one ID.
I am using PSManager to manage the ProfileService, but even with people not using this, I’ve seen the same posts with just using ProfileSevice.
This is how the data is being done when player is loading
local function loadPlayer(plr)
if (tfind(loadedPlayers,plr)) then return end
tinsert(loadedPlayers,plr)
for storeName, storeDefaults in pairs(PROFILE_STORE_DEFAULTS) do
local store = ProfileStores[storeName]
if (not store) then
store = loadStore(storeName)
end
local profileKey = processProfileKey(plr,storeDefaults._playerProfileKey)
local profile = store:LoadProfileAsync(
profileKey,
DEFAULT_NRH
)
if profile ~= nil then
profile:Reconcile()
profile:ListenToRelease(function()
store[plr] = nil
plr:Kick("Profile released.")
end)
if plr:IsDescendantOf(PLRS) == true then
local dummy = {
Data = {}
}
local callbacks = {}
dummy.Data.__index = function(t,i)
return profile.Data[i]
end
dummy.Data.__newindex = function(t,i,v)
profile.Data[i] = v
if (not callbacks[i]) or (callbacks[i] and #callbacks[i] < 1) then return end
for _ , callback in ipairs(callbacks[i]) do
spawn(function()
callback(v)
end)
end
end
dummy.__index = function(t,i)
if (i ~= "Data") then
return profile[i]
end
end
dummy.__newindex = function(t,i,v)
profile[i] = v
end
setmetatable(dummy.Data,dummy.Data)
setmetatable(dummy,dummy)
function dummy:OnDataValueChanged(dataIndex,callback)
if (not callbacks[dataIndex]) then
callbacks[dataIndex] = {}
end
table.insert(callbacks[dataIndex],callback)
local Connection = {}
function Connection:Disconnect()
table.remove(callbacks[dataIndex],table.find(callbacks[dataIndex],callback))
end
return Connection
end
task.spawn(function()
local globalUpdates = dummy.GlobalUpdates
local activeCount = 0
for index, update in globalUpdates:GetActiveUpdates() do
activeCount += 1
globalUpdates:LockActiveUpdate(update[1])
end
local lockedCount = 0
for index, update in globalUpdates:GetLockedUpdates() do
lockedCount += 1
handleLockedUpdate(globalUpdates, update, profile)
end
globalUpdates:ListenToNewActiveUpdate(function(id, data)
globalUpdates:LockActiveUpdate(id)
end)
globalUpdates:ListenToNewLockedUpdate(function(id, data)
handleLockedUpdate(globalUpdates, {id, data}, profile)
end)
end)
store[plr] = dummy
else
profile:Release()
end
else
plr:Kick("Couldn't get data. Please try again later.")
end
end
tinsert(fullyLoadedPlayers,plr)
end
And when the player is leaving
PLRS.PlayerRemoving:Connect(function(plr: Player)
if (not tfind(loadedPlayers, plr)) then return end
tremove(loadedPlayers, tfind(loadedPlayers, plr))
if (tfind(fullyLoadedPlayers, plr)) then
tremove(fullyLoadedPlayers, tfind(fullyLoadedPlayers, plr))
end
for storeName, store in pairs(ProfileStores) do
if typeof(store) ~= "table" or ( typeof(store) == "table" and not store[plr] ) then continue end
store[plr]:Release()
store[plr] = nil
tremove(store, tfind(store,plr))
end
end)
Looking at your .PlayerRemoving and .PlayerAdded event, the code looks fine and doesn’t seem to have issues.
However, I strongly advise you not to use PSManager, as it is not updated and prone to bugs and issues. I use profile service in my game and I haven’t had an issue like this yet. I suggest you update the ProfileService module to the latest version and also use ReplicaService over PSManager as it is developed by the same person who made ProfileService.
Just to give you a heads up about ReplicaService, its used with player data from profile service, it’s really neat because it has events like :ListenToNewKey, :ListenToWrite, :ListenToChange and more you can check all of these events in the wiki link I sent you.
Also, this model has examples on how it’s used with profile service and by itself:
How does ListenToChange work? I’m trying to get it to work but it won’t seem to want to work
(completely recoded PSManager by the way for ReplicaService, etc)
local function PlayerAdded(player : Player)
local playerProfile = PSManager:GetFullProfile(player)
local profile = playerProfile.Profile
pcall(function()
table.insert(profile.Data.Joins, os.time())
end)
for i, v in PlayerTemplate do
pcall(function()
playerProfile.Replica:ListenToChange({i}, function(new)
print(i, new)
DataService["Stat Changed"]:Fire(player, i, new)
DataService.Client["Stat Changed"]:Fire(player, i, new)
end)
end)
end
end
It just doesn’t print anything but the value is changing
This is how the code was written before changing PSManager
local function PlayerAdded(player : Player)
local data = GetProfile(player)
pcall(function()
table.insert(data.Data.Joins, os.time())
end)
for i, v in PlayerTemplate do
pcall(function()
data:OnDataValueChanged(i, function(new)
DataService["Stat Changed"]:Fire(player, i, new)
DataService.Client["Stat Changed"]:Fire(player, i, new)
end)
end)
end
end
I forgot I had it wrapped in a pcall and wasn’t seeing the error, this is the error
ListenToChange is a event that can be accessed on the client side of replicaservice.
On the server you would use the mutators to change variables in data and on the client you would listen to these changes via these functions
Basically replica service removes the need to use remote events. So wherever you’re making changes to data you need to replace them with the mutator functions. For example:
Server:
players.PlayerAdded:Connect(function()
local dataClassToken = replicaService.NewClassToken('PlayerData')
local newReplica = replicaService.NewReplica({
ClassToken = dataClassToken, -- important for the client side as this is how you will access the data on the client
Data = {["Coins"] = 0}, -- the profile you load from profile service
Replication = selectedPlayer -- only replicate the player's data to the player
)
newReplica:SetValue({"Coins"}, newReplica.Data.Coins + 10)
end)
Client:
local replicaController = require(replicatedStorage.ReplicaController)
replicaController.ReplicaOfClassCreated("PlayerData", function(playerDataReplica)
playerDataReplica:ListenToChange({"Coins"}, function(newValue, previousValue)
print("Coins has been updated to: "..newValue.." from: "..previousValue)
end)
end)
This example the server is using the mutator “SetValue” in the path “Coins” and the client is listening to this change by using ListenToChange
So there’s no way to figure out if value changed through the server? To be honest, I feel like it’s too much just to use replicaController to see when the value changes, the event makes it 10x easier and I won’t have to access a whole new module I’ll only use once. Being able to just access it through the server and know when the value is changed, it’ll help alot more. If it’s not possible, I guess I’d just have to do it that way which sucks (not literally but yk what I mean). And some of the server code also uses the bindable event on the server to know when a value is changed, especially for something like settings, etc.
Yeah, to put it short, there is no way to get change detections on the server with replica service. But for a good reason, however if you feel you’ve gone too far with your current server code to switch to ReplicaService, then it’s fine. You’d save time deep diving into your source code to analyze the problem instead of rewriting your entire network.
I mean everything is working great already with ReplicaService, it’s just that you cannot access value changes through on the server, kinda of a dumb decision for the developers to not do, but that’s not something I’d go more into.
You can see here, I mainly use it on the Server than the client.
Yeah, loleris, the developer, likely didnt make this because wherever you’re implementing the modification, you can use the server mutators. These mutators are specifically made to alert the client through listeners. To put it differently, ReplicaService is structured so that whenever you modify a value via the API, it automatically notifies the client.