MessagingService quota: why are my publications throttled?

Hi there!

Our publications to MessagingService are getting throttled, but I don’t understand the reason.

In our game, we use MessagingService to send out 1 message to a single topic per 5 seconds per server. The messages are well under 1 Kb.

This worked fine, until we get about 10 servers, and we start getting warnings that the rate of messaging exceeds the limit, please retry after X seconds.

As our number of servers climbs a bit futher to 15-20, publishing breaks down even further and many messages don’t make it through. The warnings are now often accompanied by another warning:
MessagingService:PublishAsync(): Too many publish requests, please try again later.

When I look up MessagingService, it seems to me that our game is staying well within the listed quota limits.

I am sure that each server only publishes once per 5 seconds, so 12 times per server per minute. I thought maybe the receiver scripts would have a hard time at some point, but those are in another place that only listens and doesnt publish any messages (nor does it give any indication of using up any significant resources). The warning (and reason for the messages not coming through) is on the sending servers. The script is fairly simple, a while true loop with publishAsync() and wait(5), I can’t imagine it going wrong and sending too often, and nothing else in our game universe uses PublishAsync().

Could anyone explain to me if I’m doing something wrong, or not understanding the quota mechanics? Or could these throttles be a bug?

Thanks for any advice or help!

Edit: Sorry, I said warnings, but they are probably errors: My PublishAsync call is wrapped in a pcall, so I catch the error and warn() the error message. There is no retry logic.

Edit 2: Posted my code below in a reply, and slightly improved the symptom description in this post.

1 Like

10 servers * 12 messages / minute = 120 messages/minute total

The only limit that comes close to would be “Messages received per topic” which for 10 servers is 210/minute.

Are you only receiving in a single place? Is it possible you’re subscribing per-player and not unsubscribing?

1 Like

Well, would subscribing multiple times to receive messages really each count to the quota?

Naturally, we subscribe once per listening server. But I thought subsciptions to a topic was a separate issue, and if each message is only counted once (no matter how many listeners), I should always stay under (10 + 20 * number of servers) per minute… Because I would send (12*number of servers) per minute…

Thanks in any case!! Good suggestion. I’ll double check for multiple topic subscriptions bugs, just to be sure.

Edit: Double-checked it and I don’t think there can be any bugs with multiple subscriptions per server. I did notice that if the throttle were based on the number of online servers for the main place of our game universe (instead of all the places in our universe), the throttle would be suspiciously close to correct…

I would really like any help on this. This problem is in our live game, and because of it, people are unable to join their friends from our lobby server. You can see the result here:
https://www.roblox.com/games/1097288251

I will post my code. It should work for servers with up to 70 players or so (after that the messages may get too big), but our places are max 30 or 40 people per place.

As described, the sender script gets throttled a lot and throws mentioned warnings/errors, and nothing else in our game uses MessagingService. The scripts are in separate places: our lobby only receives the player counts, and the other places all only send it.

I really tried to trim it down to the basics, I don’t see what could be going wrong, apart from the quota working different from the description.

Sending script:

local messagingService = game:GetService("MessagingService")
local playerService = game:GetService("Players")

local myMessage = {}
myMessage["Server"] = game.JobId
myMessage["Place"] = tostring(game.PlaceId)

local MESSAGING_TOPIC = "ServerInfo"

local function publishMessage(message)
	local publishSuccess, publishResult = pcall(function()
		
		messagingService:PublishAsync(MESSAGING_TOPIC, message)
	end)
	if not publishSuccess then
		warn(publishResult)
	end
end
	

game:BindToClose(function()
	myMessage["Players"] = {}
	myMessage["Amount"] = 0
	publishMessage(myMessage)
end)


while true do
	myMessage["Players"] = {}

	local playerCount = 0
	for i, plr in pairs(playerService:GetPlayers()) do
		table.insert(myMessage["Players"], tostring(plr.UserId))
		playerCount = playerCount + 1
	end 
	
	myMessage["Amount"] = playerCount
	
	publishMessage(myMessage)
	
	wait(5)
end

Receiver script:

local messagingService = game:GetService("MessagingService")

local ServerScriptService = game:GetService("ServerScriptService")
local peopleOnlineFolder = ServerScriptService:WaitForChild("PeopleOnlineFolder")

local PlayersOnlineModule = require(peopleOnlineFolder:WaitForChild("PlayersOnlineModuleScript"))

local MESSAGING_TOPIC = "ServerInfo"

local subscribeSuccess, subscribeConnection

while not subscribeSuccess do
	 subscribeSuccess, subscribeConnection = pcall(function()
		return messagingService:SubscribeAsync(MESSAGING_TOPIC, function(message)
			
			--print(message.Data.Players)
			--print("My server: ", message.Data.Server)
			PlayersOnlineModule:UpdatePlayersOnServer(message.Data)
			
		end)
	end)
	if not subscribeSuccess then
		warn(subscribeConnection)
	end
	wait(5)
end
1 Like

hey @Nonaz_jr - to clarify, do you have the same servers sending and also receiving messages?

I believe what’s happening is that it’s not actually 10 servers * 12 messages / minute, but actually 10 servers sending * 10 servers to send to * 12 messages / minute, which is 1200 messages / minute…

2 Likes

EDIT: Really sorry to waste your time, I found the answer here:

Hi!

Thank you so much for helping us! As I wrote, there should be no servers that are both sending and receiving messages. The only explanation I could come up with if that really were the case, is an undiscovered virus using MessagingService, which seems really unlikely to me.

In our game universe, we have 5 different places. We don’t use a single script architecture, and our implementation of this feature is pretty straightforward. Every place has a copy of this folder in ServerScriptService:
image

There are a script ‘PlayersOnlineSender’ and ‘PlayersOnlineReceiver’. Always one is toggled off (disabled), and one is active (enabled). These 2 scripts in each place are the only things in our whole game universe using MessagingService. The code of these scripts in their entirety can be seen in the post above. The folder or scripts are not copied at any time, and no other scripts change their activation; either only the sender or the receiver is toggled on from the start, and nothing changes that.

The starting place is the intro lobby, where the receiving script is active and the sending script is disabled (as in this screenshot). The other four places have the receiving script disabled and the sender script enabled. Sending and receiving are in no way connected on any single server.

If you are able to open our live game, you will be able to verify this. However, we have now greatly lowered our refresh rates, and our player count has gone down somewhat lately, and we haven’t been seeing the warnings since a few days to a week. The intro lobby now also shows the correct player counts again, albeit only after a while after initialization of the server (due to low refresh rates).

We were hoping this system would hold up to a few thousand concurrent players at least, and eventually break for reasons unrelated to MessagingService quota. Do you have any idea what could be the problem? It’s always possible I have made a mistake, if so I am so sorry, but I have spent quite some time and effort on trying to find out what’s wrong.

Finding your friends easily is a high priority in the multi-place game we are making. This feature was originally a workaround for this problem: GetFriendsOnline is unreliable
Thank you so much for your support!

1 Like

(see edit below)
Or… Did I misunderstand? Do you maybe mean to say that if I have 10 servers listening to a topic, and 10 other servers sending a single message to that topic, that counts as a total of 100 messages to the quota?? And if so, is that intentional??

If that is the case, that’s really surprising to me. I expected that the messages received per topic are the messages ‘received by the topic’, regardless of how many listeners are subscribed to that topic. Places with implementations like this one < Teleportation Playground - Roblox > (sending and receiving) would quickly break as the quota count of total received messages would be a second order polynomial of the number of servers… Yet maybe that is why you asked… Could you please clarify on the matter? Sorry if I misunderstood.

EDIT: I’m so sorry to waste your time. It is indeed as described here, as confirmed by below forum thread, I found it after a lot of digging. I will have to find another way to track friends online. It could be helpful if the docs could be more clear about this. Thanks for helping us!

1 Like

@Nonaz_jr you didn’t waste my time! I agree with you on that the documentation needs to be updated with this info. I’ve made that as an action item for us, and we’ll look to add some examples on how you can update your app to get around this.

apologies for the late response.

3 Likes

Thank you, your response was actually really fast.

I would appreciate that, but I think fundamentally MessagingService cannot be applied to this use case. To individually track up to 200 friends per player, we would have to either be able to subscribe to thousands of topics (up to 202*playercount) per server, or send 200 messages per player in a short time (so that each player only needs to listen to their own channel)… I don’t see yet how it could work given the limitations.

I will shortly elaborate on what we tried so far, feel free to skip.

It’s of huge importance to be able to join your friends in the instances of the places behind our intro lobby. And it will become even more so as Roblox further becomes a metaverse.

As explained, player:GetFriendsOnline is the best candidate, but displays players in our game as offline when they are at the same time browsing the website.

Players:GetFriendsAsync() doesn’t give the job id / place id.

TeleportService:GetPlayerPlaceInstanceAsync should probably not be spammed 200 times per player, but a batch (overload table of plrIds) functionality could be nice. I may use this function to correct player:GetFriendsOnline.

Roblox endpoints could be used, but HttpService blocks access to them. Using a proxy doesn’t seem right.

Datastores are not suitable given the even more stringent quotas (though at least doesn’t scale quadratically).

My best option at this point seems to be getting a private external server to replace the MessagingService functionality (so not a proxy using the endpoints). But I really wanted to do this with only Roblox platform tools…

The current solution was intended as a placeholder until the final target state using Universe scripts. Hopefully their release will provide a ubiquitous solution!

Any information on that, or further ideas, are welcome! Hope this overview can provide some more insight in the matter.

2 Likes

This seems like a bug. Will ask around about it.

I think making batch calls in general for things like this is a good add. I’ll see if we can add this to our roadmap.

My team is responsible for Universe Scripts so we’ll keep a note of this!

4 Likes

You’re the best!

When I wrote about GetFriendsOnline in this thread, @EtiTheSpirit did mention to us that this behaviour was in fact intended.

EDIT: As I mentioned in that post, a future event-based solution would be much much preferable to any polling based solution. Because the players’ location must be updated as soon as possible, to prevent a teleport to the wrong server, or worse, a server already shutting down.

2 Likes