Introducing MemoryStore - High Throughput, Low Latency Data Service!

I’m having an issue with :GetRangeAsync()

It says it returns a table with the keys and values sorted, however I am getting a key with the value 1 and a table value?

image

In the example it shows this however?

image

I only get the proper data if I use :GetRangeAsync()[1] which makes no sense at all.

The example would look like this:

{
	[1] = {
		["key"] = "2021",
		["value"] = "cool"
	},
	[2] = {
		["key"] = "alpha",
		["value"] = "hello"
	},
	[3] = {
		["key"] = "apple",
		["value"] = 34565
	},
	[4] = {
		["key"] = "banana",
		["value"] = "455721"
	},
	[5] = {
		["key"] = "player_4634",
		["value"] = "876343"
	}
}
3 Likes

Yep! Both in this way (long polling eats up nearly the entire Http budget pretty easily), and also the fact that most of the big cloud companies charge heavily for these kind of reqeuests (particularly when you have a big response).

1 Like

Is this meant to be here?

It says it lists all, but on the API page it says

But then in studio it gives an error and says it must be between 1 and 100.

image

Is this going to be changed? Will it eventually be able to pull all key values? or 1-200? or stay at 100 like it is now?

3 Likes

UPDATE:

They took my suggestions into account. Either that or I’ve failed to read the first few paragraphs; I think it’s the former.

3 Likes

Memory stores are going to help a ton when doing matchmaking, but I find that some functionality to help us control this more is missing, due to two reasons. I made feature requests for both, but the use case I have for them is very connected and so I think it’s worth detailing here:

  1. There’s no way to tell if a player in queue should still be in queue

    • You could only tell if the server which processes the player is the same server the player is/was in, which is very unlikely
    • The only way to remove items from queue is through the string identifier returned by ReadAsync, but due to the limits this would be impossible to read items one by one just to get an identifier for removing it
    • Even if not for limits, you’d have to make items go invisible to achieve this which is incredibly inefficient.
    • The solution to this would be a way to remove a specific item from queue without having to read it, which I made a feature request for here: MemoryStoreQueue:AddAsync() should return a string identifier for removing the added item
  2. Servers players arrive in don’t have the ability to see which players are still on their way over, which have left the game, cancelled joining or maybe failed to teleport for whatever reason.

    • Having the ability to understand from the destination server which players are still in teleport would let us wait for all players to arrive without assuming players not in teleport are still in teleport.
    • In a case where a player is no longer on their way, we currently can’t see that immediately and react to it by pulling a replacement player from the queue. This keeps our players waiting longer than necessary.
    • In a case where a player is still on their way but taking far too long to arrive, we currently can’t know whether or not they are on their way still. This isn’t as important as we could pull a replacement player, and should the original player arrive we can send them back to the lobby. This is less ideal than just being able to cancel their teleportation from the destination server.
    • New functionality to TeleportService could solve these problems, which I made a feature request for here: TeleportService should give more insight and control over traffic to the current server

Overall, we need more control over our queues and over our teleportation traffic. Without these, matchmaking will end up being slower and players will end up in cases where they must wait longer before playing.

10 Likes

Some of my thoughts on this. Currently it’s a little weird to work with.

There’s no way to clear all memory easily unless you want to read the entire queue and then remove all with the identifier which doesn’t really make sense.

The given example of matchmaking isn’t possible with MemoryQueue (I’m working on a MatchmakingService right now and ran into this problem, I plan on making this open source and posting it in #resources:community-resources when it’s done, but I don’t have a ton of time to work on it right now due to college). It’s not possible because we can’t dequeue a specific player unless they’re at the front of the queue which is not something you can guarantee. We would need a MemoryQueue:RemoveAsync(Variant item) that would remove a specific item from the queue regardless of its position. Otherwise what if a player dc’s while they’re queued? Their id just sits in the queue with no way to remove it until they actually come up in the queue. I switched to using a sorted map with a table of all user ids that are queued in a specific skill group. I spent a long time trying to get this to work with a memory queue but I could not think of a good way to do this. But with a sorted map I can just do this:

function MatchmakingService:RemovePlayersFromQueueId(players, skillLevel)
	local memoryQueue = MemoryStoreService:GetSortedMap("MATCHMAKINGSERVICE_QUEUE")
	memoryQueue:UpdateAsync(tostring(skillLevel), function(old)
		if old == nil then return nil end
		for _, v in ipairs(players) do
			local index = table.find(old, v)
			table.remove(old, index)
		end
		return old
	end, 86400) -- 86400 is for testing but isn't really necessary
end


function MatchmakingService:RemovePlayerFromQueueId(player, skillLevel)
	local memoryQueue = MemoryStoreService:GetSortedMap("MATCHMAKINGSERVICE_QUEUE")
	memoryQueue:UpdateAsync(tostring(skillLevel), function(old)
		if old == nil then return nil end
		local index = table.find(old, player)
		table.remove(old, index)
		return old
	end, 86400) -- 86400 is for testing but isn't really necessary
end

which is all in one update operation. I can call the RemovePlayerFromQueue method when they disconnect and guarantee they won’t be accidentally found in the queue.

Basically all I would like to see is a way to clear all the memory at once, clear specific sorted maps and queues in one operation, and a way to remove a specific item from a memory queue without reading it first.

I didn’t see that @Extuls had basically the same grievances as I did with a matchmaking system. They made a great suggestion to return an identifier to remove it from the queue when you add it which could also work if a RemoveAsync(Variant item) isn’t feasible.

6 Likes

After playing with MemoryStoreQueue’s
I get the following error with
ReadAsync

Code: 2, Error: The name field is required. (00-20dc315841721445bfecce160e646979-9cb72dd19b1dd94f-00)

The string passed as a name for the queue is "test"
In documentation and studio, :ReadAsync() does not take a name argument.

When adding to the queue with AddQueue, it does not specify a name argument either, so no key is given in a Queue. I believe this is a bug.

Should I be using this instead of TeleportData?

If you’re worried about the security of teleport data in your case then I would say this is a great option for bringing players’ data to a new server by saving their data to a sorted map with a short expiration time.

If security isn’t at all a concern in your case then teleport data should be sufficient enough.

1 Like

I’ve been reading the Requests Quota of this service and I’ve come to a misunderstanding, it says that the API requests have a rate limit of *1000 + 100 ⨉ [number of users], but below it says that “the rate of requests to any single queue or sorted map is limited to 100,000 requests per minute.”

What is the maximum requests per minute? If you have over 1k players, will the limit still be 100,000 requests per minute?

Sorry if this was specified earlier, but I couldn’t find any information. By the way, this is a nice service to be added!

The upper limit for your total requests is 1000 + 100 ⨉ [number of users]. We also have the 100K requests/min limit for a specific data structure no matter how many users you have. It’s similar to the per key request limit for DataStore. The throttle will be triggered when any of the criteria is met.

1 Like

Basically, the total amount of requests that can be made in total is * 1000 + 100 ⨉ [number of users]. My requests to, however, MemoryStoreService:GetSortedMap(“Matchmaking”) will be limited to 100k requests? Or only a key into that map will be limited to that number? Sorry if I didn’t understand this correctly.

EDIT: Also, there seems to be a bug when Queues have 0 invisibility timeout. After trying to remove the items after getting them, the item would still remain in the memory. This thing doesn’t happen if invisibility is something greater than 0 seconds. Is this intended?

local MemoryStoreService = game:GetService("MemoryStoreService")
local Queue = MemoryStoreService:GetQueue("MATCHMAKING", 5)
Queue:AddAsync({
	Id = 500
}, 300)
Queue:AddAsync({
	Id = 500
}, 300)
Queue:AddAsync({
	Id = 500
}, 300)

local Data, Id = Queue:ReadAsync(10, false, 0)
print(Data)
Queue:RemoveAsync(Id)

task.wait(10)

local Data2, Id2 = Queue:ReadAsync(10, false, 0)
print(Data2)

EDIT 2: MemoryStoreQueue:ReadAsync()'s waitTimeout parameter states that if you can wait indefinitely by putting the value to -1. Is it truly indefinitely or is there a limit?

I have a question. I want to use memory store for matchmaking in my fps game, but I am worried about possible latency issues between player and server. Memory store works global, isn’t it? If I teleport some player to server which there is a high delay between the player and the server, this will affect gameplay quality. Is there a solution for this or am I lacking knowledge about it?

The expiration parameter of MemoryStoreSortedMap:SetAsync() and MemoryStoreSortedMap:UpdateAsync() desperately needs some clarification.

On the Developer Hub, it states that the expiration parameter takes an Int64 value, which can technically get up to 9,223,372,036,854,775,807

When you set a value that high, no error is thrown but no value is set either (very cool, not frustrating at all). When you set a somewhat lower value however, you get this error:

image

This is still a lie however, by a few decimal places. After testing different values, I found that they actually cap the expiration value to 2,592,000, not 2,592,000,000. It was only after I put in a value less than 2,592,000 that it actually started saving my data.

Poor documentation of new features is happening way too often. Love the new stuff, but it’s critical that we have the ability to learn how to use it properly through official channels.

11 Likes

Anything about the memory size limit that’s in bold still being wrong? This issue still seems to be ongoing and values still seem to be capped it seems as I am getting “The provided value is too long” for just over 1kb in a sorted map which makes this incredibly difficult to work with because 1kb is nothing.


Here’s an image, as you can see the error occurs when the length of the entire data is just over 1kb (1024). This is affecting a lot of people that use my module as the documentation is incorrect when it makes this claim:

I know that the entire memory isn’t over 65kb as well. There’s maybe 2kb in there at max.

Here’s also confirmation. I made a new game with 0 memory usage. And I made a test script and confirmed these suspicions:

It’s fine on the first one, which is (just) under 1kb. But it errors on the second one which is over 1kb. And the total memory limit with 1 player is 65kb, which this is definitely not hitting.

And before you say it’s because I’m in studio, it’s not:

Simple test code, literally just a string of characters
local MemoryService = game:GetService("MemoryStoreService")
print("Memory limit size test:")
local test = ""
for i = 1, 1020 do
	test = test .. "1"
end
print("Length of data being added: " .. tostring(#test))

local map = MemoryService:GetSortedMap("Map")

map:RemoveAsync("Test")
map:RemoveAsync("Test1")

map:SetAsync("Test", test, 86400)

test = ""
for i = 1, 1100 do
	test = test .. "1"
end

print("Length of data being added: " .. tostring(#test))

map:SetAsync("Test1", test, 86400)

Please either fix the memory size limit on sorted maps or document it correctly.

8 Likes

I could be wrong but I think the error message for exceeding the expiration time is wrong.

I tried to use that exact time and it wouldn’t let me. That’s 30,000 Days rather than 30?

Nice, This is going to be so useful. Question: Does MemoryStore Data save accross places? And how can I use AddAsync? Do you type in the name of the value, The value itself then its priority then its experition? Because I need this to temporarily store player data for a game.

so don’t use Queues but SortedMaps

This service is useless for matchmaking. You’d expect to implement this using Queues, but this currently has no functionality to remove a specific item. You can’t account for players exiting the queue.

You could probably implement a hacky solution using SortedMaps, but this seems counterintuitive and seemingly makes Queues futile for matchmaking systems, which is what I was personally excited for.

Not sure if this was an oversight or some sort of limitation but seeing such a basic crucial feature missing is really disappointing for such a seemingly powerful service. :frowning_face:

4 Likes