Introducing MemoryStore - High Throughput, Low Latency Data Service!

When this service was initially announced, singular SortedMaps did not have a size limit as long as it did not go over the quota. Since then it was lowered to 1kb. I made a bug report about this before the docs were updated:

This just made it very challenging to use SortedMaps because 1kb is not a lot to work with.

As for memory queues, I would just like the ability to control them a bit more, such as getting specific values from the queue, or just added a Skip parameter to ReadAsync that will skip the number you give it and only return the values after. There also needs to be a way to remove specific items from memory queues.

The example for memory queues you guys give is a matchmaker. However, if a player leaves after they queue, there’s no way to remove them unless you read them first, which is rather inefficient because you also can’t just remove a single value while reading, it’s either you remove everything you read or nothing.

3 Likes

I will also share my opinion on this topic.
SortedMaps are fine (except the 1kB limit), but Queues need to be reworked from scratch.

Here are the API members they should have:

:AddAsync(value: any, expiration: number, priority: number?)


This function stays the same.

:RemoveAsync(value: any)


Removes all items that are equal to value.

:UpdateAsync(transformFunction: (oldValue: {any}) → {any}, expiration: number)


Retrieves the queue and lets you update it via a callback function. The queue is a sorted array.

:GetRangeAsync(count: number?, direction: Enum.SortDirection?): {any}


Returns a sorted array of queue items. count specifies the number of items (if nil, all are returned). direction specifies whether it starts at the end of the queue (descending) or at the beginning (ascending). The default is Enum.SortDirection.Descending.
It would be nice if this function didn’t have a limit of how many items it can return (or at least bigger than 300).

.Added / .Removed / .Changed


We need some sort of event when the queue got updated.


Please make ReadAsync deprecated and remove it after some time. It is extremely inefficient.

I hope that you will realize at least a few of these functions. If you need a more detailed explanation, feel free to ask me. :slightly_smiling_face:

1 Like

Hello. Thank you for the response. As was mentioned by @Extuls and @steven4547466 there is no efficient method including the one you have described. A thread that has more detail can be found here.

As for your question:

Let’s use ReadAsync for example, 20 results. 10 of those results are players that have left the matchmaking queue. That leaves us with 10 valid results. How can I remove those 10 invalid results, so that I only get 20 valid results?

There is no way to tell which results are valid or not. Even if there was a way to tell if a player was “dropped”, RemoveAsync would only let you remove all 20 results from the queue.

The reason most of us would prefer to have a specific amount of valid results from a Queue is so that we can make sure that there are enough players to start a match. Not to mention there is also that chance that TeleportService may fail, and some matched players aren’t able to make it. Being able to account for the margin of error would be incredibly useful.

All of this is not only to make life easier for us developers trying to make a matchmaking system but, will greatly improve user experience. We want players to be matched as efficiently and swiftly as possible.

What would be a potential solution is that instead of AddAsync() returning void, it could return an id that you can use in conjunction with RemoveAsync(). That way, if a player leaves the game, the queue, or whatever else we could handle this logic ourselves.

1 Like

Thanks for the details. Can you share more about your use case? What type of data you’d like to store in sorted map? What’s max size do you expect?

Some of the functions/events proposed seem too complicated and some may not be that practical in the long run. Returning all items in the queue for example, what if there are thousands of items? The same goes for the events, I think it would be more efficient to make a simple loop to fetch the queue instead of listening to events being fired hundreds of times a minute.

Also, why would you want the option to fetch the very back of the queue? The point of a queue is so that the elements in the front of the queue get processed first.

My main use case is for my module MatchmakingService. Before the documentation was updated, it stated that, as long as you don’t go above the quota, SortedMaps had no limit. I was just wondering why that was scrapped for a 1kb limit. For example, I hold running games in memory to support adding players to games that have already been made. This allows players to start games with the minimum number of players required so they can just get in and play, and more players can join if spots are open. These games typically don’t last long so I hold them in a sorted map.

The problem with the 1kb limit comes with keeping track of running games, which are divided two ways: map and rating type. My service supports skill-based matchmaking (sbmm) which allows developers to enable it if they want a rating system for their game, they can have as many ratingTypes as they want so they can have multiple queue types (think “ranked” and “unranked” that both use sbmm, but for a different reason and aren’t connected). It also supports an arbitrary number of maps so that developers can have more than one map if they choose. All this boils down to is a lot of usage in memory, that doesn’t go over the size quota, but will go over the 1kb limit. I don’t save every game in the same sorted map, each game gets their own unique code.

In order to support adding players to running games I keep a list of every game code and put it into a group based on the rating type and map. It would be inefficient to use GetRangeAsync on every running game if none of the running games apply to the player’s queued game type and map, so (see bottom) I need to keep a list of every running game’s unique code for every map and rating type (I don’t store the entire game, it’s just a list of strings that represent the games), and at about 20 games that list exceeds the 1kb limit so I need to break that list up every 20 or so games, so I just have a counter that keeps track of every new sorted map so I can retrieve the list of running games based on rating type and map. I could shove more into the sorted map by reducing the size of the unique codes for every game, but eventually I’d still run into this problem, it’s unavoidable.

It just gets very complex very quickly with such a small limit and I’m really upset that the original unlimited size for sorted maps, as long as it didn’t exceed the quota, was scrapped for such a small limit.

I may switch to GetRangeAsync just to make it easier on myself to maintain, but, at least in my opinion, it shouldn’t be necessary. After re-reading the documentation on GetRangeAsync it wouldn’t be a suitable replacement as it only works on a single sorted map, which doesn’t help me in this case as I would need to get a list of all games running which are all different sorted maps. I could put all the running games into a single sorted map as, after re-reading the docs each value in the sorted map has the 1kb limit and maybe that could work, I’ll have to experiment with it though. However, the limit being so small is still not ideal.

I doubt that there would be thousands of items in a matchmaking queue. If you maintain it in a first-in-first-out (FIFO) sequence, the maximum player count in the queue is approximately the player count in the matches. I have never seen matches with more than 100 players on Roblox.
If you don’t use FIFO, there still won’t be thousands of items. As I said in the post, I would be satisfied with a limit (of how many items it can return) that is 300 or bigger.

Polling is better when there are many queue changes and events are better when there are not many changes. The best would be probably a polling loop that adapts the amount of requests to the player count.

My bad, I mixed up the start and end of a queue. The default for direction should be Enum.SortDirection.Ascending.
I thought that it would be nice to have, but I wouldn’t know a use case for Descending right know either.

Do you have any other concerns?

Thanks for the details. We thought you probably can have a sorted map to track who have dropped from the queue, but it seems to add more complexity. We are discussing internally what’d be the best way to handle this. Stay tuned!

6 Likes

Wow this will be very helpful as I’m making a matchmaking queue for my game as well as the global marketplace. Can’t wait to try this out!

See the updated post. Size limited increased to 32KB :upside_down_face:

6 Likes

That’s amazing! I’m not sure who works on the developer articles, but the memory store service article should be updated to reflect this as well.

https://developer.roblox.com/en-us/articles/memory-store

Yeah, we’ve notified the documentation team, but they haven’t got a chance to update yet.

MessagingService is for sending messages between servers in real-time, not for persisting information in a central location in certain data structures. DataStoreService is not sufficient for this because datastores is for high latency, low-throughput (but long persistent) data.

If you don’t understand the benefit from this post, I would come back to it at a later point once you have more experience with DataStoreService/MessagingService.

Currently working on quite a complex system using a combination of memory store service + messaging service and I wish to validate that I am cleaning up 100% data since due to the nature of my system I do not wish for data to automatically expire.

Would it be possible for there to be some sort of method for developers to check how much of both the memory size and requests quota we have left to utilise?

3 Likes

Please consider adding this feature as well: MemoryStoreQueue:AddAsync() should return a string identifier for removing the added item

I’m not going to pretend that I’m in any way familiar or knowledgable about how memory store is working internally, I assume it uses some form of binary search hence why it’s a sorted map and that’s the basis that I would make the request that:

It would be very useful to be able to get the position of a key relative to all the other keys within a sorted map.

For example if I’m storing the keys: ‘A’ , ‘B’ , ‘C’ it would be incredibly useful to be able to get the position of ‘B’ which would be 2.

This would allow myself and other developers to make large leaderboards using exclusively roblox allowing us to show a player their position on a leaderboard without caching a ton of data on each server.

1 Like

To me, this is man, our data is absolute just like coroutines. I’m ready to see faster world loading with higher details and it’s happening fast. :sunglasses:

We’re looking at the use case. The solution may not be exactly the way you recommended, but we will try to find the best way to solve it.

1 Like

Can you share more about the use case? Do you want to quickly get the rank of a player without reading the whole map?

1 Like

Yeah, that’s basically my intended use case, I considered I may be able to use some form of an adjusted binary search to try and locate the player but I found myself also running into the issue of efficiently locating the midpoint + this solution would require a fair amount of requests per operation.

The system im trying to build is a leaderboard where I can tell a player their precise position for example if they are #53,498th in the world without using any external service


Edit: was lying in bed when I released this could easily be mis interpreted so just for extra clarity:

I’m requesting the ability to get the position of the key, not the ability to get a key at a position

1 Like