I am trying to track the number of copies spawned of specific types of pets. I have run into multiple technical issues, and I think it’s worth the while to list them.
Technical Issues:
- Roblox has an experience limit of 6 seconds for write requests.
- Any server can spawn in a new pet and that pet needs to be accounted for.
- Copy counts cannot be updated immediately due to technical issue #1.
Potential Solution: Have one server act as a host to complete the updates, and switch hosts if that server dies.
My implementation so far is to use a combination of ProfileService and MessagingService. ProfileService provides session locking, but it doesn’t allow you to check if the server that has its session locked still exists. Because this is not a player profile and is instead a server profile, any server can try session locking. To avoid the case where the servers compete to have the session for the updating copies I need to check if the server who has the session still exists.
My code snippet is as follows:
local PLUSHIE_HOST_REQUEST = "PlushieHostRequest"
local PLUSHIE_HOST_RESPONSE = "PlushieHostResponse"
local success, connection = pcall(function()
return MessagingService:SubscribeAsync(PLUSHIE_HOST_REQUEST, function(message)
local senderJobId = message.Data.SenderJobId
local receiverJobId = message.Data.ReceiverJobId
if receiverJobId == game.JobId then
-- Respond to the server that sent this message telling it that
-- this server does, in fact, exist.
print(string.format("Server Job %s is checking if this server still exists.", senderJobId))
local success, result = pcall(function()
MessagingService:PublishAsync(PLUSHIE_HOST_RESPONSE, {
ReceiverJobId = senderJobId,
SenderJobId = receiverJobId
})
end)
end
end)
end)
local isHostAlive = false
local success, connection = pcall(function()
return MessagingService:SubscribeAsync(PLUSHIE_HOST_RESPONSE, function(message)
local senderJobId = message.Data.SenderJobId
local receiverJobId = message.Data.ReceiverJobId
if receiverJobId == game.JobId then
print(string.format("Server Job %s still exists.", senderJobId))
isHostAlive = true
end
end)
end)
for plushieType, _ in ipairs(Plushies) do
local profile = ProfileStore:LoadProfileAsync("Plushie_" .. plushieType, function(placeId, gameJobId)
local sendTime = os.time()
local timeSinceSent = 0
local success, result = pcall(function()
print(gameJobId)
MessagingService:PublishAsync(PLUSHIE_HOST_REQUEST, {
SenderJobId = game.JobId,
ReceiverJobId = gameJobId
})
end)
if not success then
warn("The message to determine the host has failed. Cancelling load.")
return "Cancel"
end
print("Sent a message to the host server. Waiting for response.")
while not isHostAlive and timeSinceSent <= 5 do
timeSinceSent = os.time() - sendTime
task.wait()
end
if isHostAlive then
print(string.format("Plushie type %s's host server is still alive. Cancelling load.", plushieType))
isHostAlive = false
return "Cancel"
end
print(string.format("Plushie type %s's host server is dead. Forcing load.", plushieType))
return "Steal"
end)
if profile == nil then
-- Another server has already claimed a lock on this profile.
print(string.format("Plushie type %d is hosted by another server.", plushieType))
continue
end
PlushieProfiles[plushieType] = profile
print(string.format("Plushie type %d is now hosted by this server.", plushieType))
profile:Reconcile()
profile:ListenToRelease(function()
PlushieProfiles[plushieType] = nil
print(string.format("Plushie type %d is now being hosted by another server.", plushieType))
end)
local globalUpdates = profile.GlobalUpdates
for i, update in ipairs(globalUpdates:GetActiveUpdates()) do
local updateId, updateData = unpack(update)
globalUpdates:LockActiveUpdate(updateId)
end
for i, update in ipairs(globalUpdates:GetLockedUpdates()) do
handleLockedUpdate(globalUpdates, unpack(update))
end
globalUpdates:ListenToNewActiveUpdate(function(updateId, updateData)
globalUpdates:LockActiveUpdate(updateId)
end)
globalUpdates:ListenToNewLockedUpdate(function(updateId, updateData)
handleLockedUpdate(globalUpdates, updateId, updateData)
end)
end
I believe that the “race” condition can still occur if every server sees that the host server no longer exists. Feedback and possible solutions are greatly appreciated!