PcoWSkbVqDnWTu_dm2ix

15 min

MemoryStoreService is a data service that provides fast in-memory storage that is accessible across servers. It offers two primitive data structures: queues and sorted maps.

In comparison to a data store, a memory store provides lower latency and higher throughput in exchange for reduced durability. It’s a good option for any data that rapidly changes, such as:

  • Global leaderboards: Store and update user rankings on a shared leaderboard inside a map with key-value pairs.
  • Skill-based matchmaking queues: Save user information such as skill level in a shared queue among servers, and use lobby servers to run matchmaking periodically.
  • Auction houses: A global marketplace where users from all servers list and bid for available goods. Store marketplace data inside a map as key-value pairs.

Limits

A memory store has both memory size and requests quota limitations.

Memory Size Quota

The memory quota limits the total amount of memory that an experience can consume. It’s not a fixed value; instead, it changes over time depending on the number of users in the experience according to the following formula:

64KB + 1KB ⨉ [number of users]

When users join the experience, the additional memory quota is available immediately. When users leave the experience, the quota is not reduced immediately; there’s a grace period of 24 hours before the quota reevaluates to a lower value.

When an experience exceeds the memory quota, the operations that consume memory return an error. To avoid such errors, either explicitly delete unneeded items or rely on item expiration. Generally, explicit deletion is the preferred way of releasing memory as it takes effect immediately. Item expiration provides a safety mechanism to ensure that unused items do not remain in memory forever.

The memory size quota is applied on the experience level instead of the server level.

Requests Quota

In addition to the memory size, the API requests (shared for all MemoryStoreService API calls) have the following limit per minute:

1000 + 100 ⨉ [number of users]

Note that the rate of requests to any single queue or sorted map is limited to 100,000 requests per minute.

The requests quota is also applied on the experience level instead of the server level. This provides flexibility to allocate the requests among servers as long as the total request rate does not exceed the quota. If you exceed the quota, an error an error response will be sent back when your requests are being throttled.

Queues

A queue is a collection of items, such as strings, that are maintained in a first-in-first-out (FIFO) sequence. In a typical queue, you add items to the back of the queue while you read and remove items from the front of the queue.

An example image of how a regular queue adds, reads, and removes items.
How a regular queue adds, reads, and removes items.

Priority of Items

While a queue defaults to FIFO order, there are situations where a queue needs to read certain items first regardless of the order in which they were added. In such cases, set a priority while adding an item; the queue reads an item with a high priority before an item with a low priority. Setting items’ priorities is useful in matchmaking, where you can set a user to a higher priority if they have been waiting for a long time.

In the following image, a user is adding an item with a set priority of 3 to a queue. The item at the back of the queue has the default priority of 0 while the item at the front of the queue has the highest set priority of 5. The new item is placed behind all items with a set priority of 3.

An example image of how an item’s set priority changes the order a queue reads items.
An item’s set priority changes the order a queue reads items.

To place an item at the front of the queue, set the priority higher than the current highest set priority; in this example, the item needs a set priority of 6 or higher.

Getting a Queue

To get a queue, reference MemoryStoreService and then call GetQueue() with a name for the data structure. The name is global within the experience, so any place that uses the same name will access the same queue. If necessary, specify a non-default read operation timeout (default is 30 seconds).

After you get a queue, call any of the following functions:

Function Action
MemoryStoreQueue/AddAsync|AddAsync() Add a new item to the queue.
MemoryStoreQueue/ReadAsync|ReadAsync() Read one or more items from the queue as a single operation.
MemoryStoreQueue/RemoveAsync|RemoveAsync() Remove one or more items previously read from the queue.

Adding Data

To add a new item to the queue, call AddAsync(), providing the item, its priority, and an expiration time in seconds.

Reading and Removing Data

To read one or more items from the queue at once, call ReadAsync(). When you are done processing items, immediately call RemoveAsync() to delete them from the queue, passing the id that ReadAsync() returns. This ensures that you will never process an item more than once.

To capture and respond to all items that are continuously being added to a queue, include a loop similar to the one within the following script:

Sorted Maps

A sorted map is a key-value data structure in which the keys are sorted in alphabetical order1; the order that you enter the keys in the map does not matter because the keys will always be sorted when you retrieve the key-value pairs.

Key Value
"2021" "cool"
"apple" 34565
"alpha" "hello"
"banana" "456721"
"player_4634" "876343"

While keys have a size limit of 128 characters, there isn’t a size limit for values as long as they don’t go over the memory size limit.

1: Numerical order may not work as expected without padding; for example “99” will sort after “100”. If numerical order is desired, pad the numbers on the left as in “00100” and “00099”.

Concurrency of Keys

Multiple servers may update the same key at the same time, meaning other servers may change the value between the last read and update. In this case, use UpdateAsync() to read the latest value as the input for your callback function so that you will always modify the latest value before updating. The latency for UpdateAsync() is similar to GetAsync() plus SetAsync() unless there is contention. When contention occurs, the system automatically retries the operation until successful.

Getting or Creating a Sorted Map

To get a sorted map, reference MemoryStoreService, then call GetSortedMap() with a name for the data structure. The name is global within the experience, so any place that uses the same name will access the same sorted map.

After you get a sorted map, call any of the following functions:

Function Action
MemoryStoreSortedMap/SetAsync|SetAsync() Add a new key or overwrite the value if the key already exists.
MemoryStoreSortedMap/GetAsync|GetAsync() Read a particular key.
MemoryStoreSortedMap/GetRangeAsync|GetRangeAsync() Read all existing keys or a specific range of them.
MemoryStoreSortedMap/UpdateAsync|UpdateAsync() Retrieve the value of a key from a sorted map and update it via a callback function.
MemoryStoreSortedMap/RemoveAsync|RemoveAsync() Removes a key from the sorted map.

Adding or Overwriting Data

To add a new key or to overwrite the value of a key in the sorted map, call SetAsync(), providing the key name, its value, and an expiration time in seconds.

Getting Data

To get one key from the sorted map, call GetAsync(). You need to set an expiration time for the added key so that the memory automatically cleans up once the key expires.

To get multiple keys from the sorted map as a single operation, call GetRangeAsync().

By default, GetRangeAsync() lists all existing keys, but you can set the lower and upper bound of the key range.

Updating Data

To retrieve the value of a key from a sorted map and update it, call UpdateAsync(), providing the key name, a callback function to update the key, and an expiration time in seconds.

The following example updates the highest bid for an auction item. UpdateAsync() ensures that the highest bid is not replaced with a lower value even if multiple servers update the same key simultaneously.

Removing Data

To remove a key from the sorted map, call RemoveAsync().

To flush your memory in sorted maps, list all your keys with GetRangeAsync(), then remove them with RemoveAsync().

Testing in Studio

To ensure that you can safely test a memory store before going to production, the MemoryStoreService offers separate namespaces for API calls from Studio versus those from runtime servers. As a result, your API calls from Studio will not access production data so that you can freely test new features. The quota you have for Studio will be very limited, similar to the minimum quota of an experience.

Debugging Production Data

To debug a memory store on live experiences, use the Developer Console. Navigate to the “Log” tab, and click “Server”. Find the “Command line” on the bottom and type the Lua code. Follow the same steps as you’d use in a Lua script to read and write the data.

Tags:
  • memory store
  • data store