13 min read

Source: Roblox Creator Hub · CC BY 4.0 · View source · Code samples: MIT Imported 2026-10-03. Formatting adapted for this site.

Memory stores

MemoryStoreService is a high throughput and low latency data service that provides fast in-memory data storage accessible from all servers in a live session. Memory Stores are suitable for frequent and ephemeral data that change rapidly and don't need to be durable, because they are faster to access and vanish when reaching the maximum lifetime. For data that needs to persist across sessions, use data stores.

Data structures

Instead of directly accessing raw data, memory stores have three primitive data structures shared across servers for quick processing: sorted map, queue, and hash map. Each data structure is a good fit for certain use cases:

graph TD
    A[Does your data need to be sorted in a specific order?]
    B[Use a sorted map.]
    C[Do you need the ability to scan all of your items at once?]
    D[Use a hash map.]
    E[Do you expect to have fewer than 1,000 keys?]

    A -- YES --> B
    A -- NO --> C

    C -- YES --> E
    C -- NO --> D

    E -- YES --> B
    E -- NO --> D

In general, if you need to access data based on a specific key, use a hash map. If you need that data to be ordered, use a sorted map. If you need to process your data in a specific order, use a queue.

Limits and quotas

To maintain the scalability and system performance, memory stores have data usage quotas for the memory size, API requests, and the data structure size.

Memory stores have an eviction policy based on expiration time, also known as time to live (TTL). Items are evicted after they expire, and memory quota is freed up for new entries. When you hit the memory limit, all subsequent write requests fail until items expire or you manually delete them.

Memory size quota

The memory quota limits the total amount of memory that a game can consume. It's not a fixed value; instead, it changes over time depending on the number of users in the game according to the formula 64 KB + 1.2 KB * [number of users]. The quota applies on the game level instead of the server level.

When users join the game, the additional memory quota is available immediately. When users leave the game, the quota doesn't reduce immediately. There's a traceback period of eight days before the quota reevaluates to a lower value.

After your game hits the memory size quota, any API requests that increase the memory size always fail. Requests that decrease or don't change the memory size still succeed.

With the observability dashboard, you can view the memory size quota of your game in real time using the Memory Usage chart.

API request limits

A request unit quota applies to all MemoryStoreService API calls. This quota is 1000 + 120 * [number of concurrent users] request units per minute.

Most API calls only consume one request unit, with a few exceptions:

The requests quota is also applied on the game 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, you receive an error response when the service throttles your requests.

With the observability feature available, you can view the request unit quota of your game in real time.

Data structure size limits

For a single sorted map or queue, the following size and item count limits apply:

Per-partition limits

In addition to the game-level request unit quota, memory stores apply request limits to each partition as a safeguard that protects the stability of the service for all games. These limits aren't a quota that your game is allocated, and they aren't a hard ceiling on the throughput your game can achieve. The following values are estimates. They are configured on the Roblox backend and can change, so don't design against them as fixed numbers.

Every request counts toward the limit of the partition that holds the item, whatever the data structure. You can currently expect throttling to begin at roughly 30,000 request units per minute for a single partition, so aim to stay well below that rate. How much of that limit a data structure consumes depends on how its items are distributed:

Hash maps have an additional limit on each item key of approximately 5,000 write request units per minute and 15,000 read request units per minute. This per-key limit applies on top of the per-partition limit, so a frequently accessed key can hit either one. The per-key limit applies only to single-key operations: the read limit applies to MemoryStoreHashMap:GetAsync(), the write limit applies to MemoryStoreHashMap:SetAsync() and MemoryStoreHashMap:RemoveAsync(), and MemoryStoreHashMap:UpdateAsync() counts against both the read and write limits. MemoryStoreHashMap:ListItemsAsync() scans partitions rather than a single key, so only the per-partition limit applies to it.

If you don't need sorting or first-in, first-out functionality, a hash map is usually the best choice because it can spread load across partitions.

When requests exceed a partition or per-key limit, the service throttles them and returns a PartitionRequestsOverLimit status code, which you can monitor with the observability dashboard.

If you expect a data structure or an item key to receive a high, sustained request rate, shard it so that the load spreads across more partitions or keys. Spread reads and writes evenly across multiple item keys to stay within per-key limits. You can also reduce the request rate by caching values on the server and rechecking them after an interval, batching requests where possible, and applying exponential backoff when you receive throttling responses.

Note

We strive to be as transparent as possible about rate limits, but additional, undocumented limits might apply, including for DDoS protection and service stability. Always ensure your game handles throttling responses. See Troubleshooting for guidance.

If you hit an undocumented rate limit that blocks your use case (or you would like higher limits), leave feedback explaining your needs in the Developer Forum.

Best practices

To keep your memory usage pattern optimal and avoid hitting the limits, follow these best practices:

Observability

The Observability Dashboard provides insights and analytics for monitoring and troubleshooting your memory store usage. With real-time updating charts on different aspects of your memory usage and API requests, you can track the memory usage pattern of your game, view the current allocated quotas, monitor the API status, and identify potential issues for performance optimization.

The following table lists and describes all status codes of API responses available on the Observability Dashboard's Request Count by Status and Requests by API x Status charts. For more information on how to resolve these errors, see Troubleshooting. For the specific quota or limit that an error relates to, see Limits and Quotas.

Status code Description
Success Success.
DataStructureMemoryOverLimit Exceeds data structure level memory size limit (100 MB).
DataUpdateConflict Conflict due to concurrent update.
AccessDenied Unauthorized to access game data. This request doesn't consume request units or use quota.
InternalError Internal error.
InvalidRequest The request doesn't have required information or has malformed information.
DataStructureItemsOverLimit Exceeds data structure level item count limit (1M).
NoItemFound No item found in [`MemoryStoreQueue:ReadAsync()`](/docs/memorystorequeue#memorystorequeue-readasync) or [`MemoryStoreSortedMap:UpdateAsync()`](/docs/memorystoresortedmap#memorystoresortedmap-updateasync). `ReadAsync()` polls every 2 seconds and returns this status code until it finds items in the queue.
DataStructureRequestsOverLimit Exceeds data structure level request unit limit (100,000 request units per minute).
PartitionRequestsOverLimit Exceeds a per-partition or per-key request unit limit.
TotalRequestsOverLimit Exceeds universe-level request unit limit.
TotalMemoryOverLimit Exceeds universe-level memory quota.
ItemValueSizeTooLarge Value size exceeds limit (32 KB).

The following table lists states codes from client side, which are currently not available on the Observability Dashboard.

Status code Description
InternalError Internal Error.
UnpublishedPlace You must publish this place to use MemoryStoreService.
InvalidClientAccess MemoryStoreService must be called from server.
InvalidExpirationTime The field 'expiration' time must be between 0 and 3,888,000.
InvalidRequest Unable to convert value to json.
InvalidRequest Unable to convert sortKey to a valid number or string.
TransformCallbackFailed Failed to invoke transformation callback function.
RequestThrottled Recent MemoryStores requests hit one or more limits.
UpdateConflict Exceeded max number of retries.

Troubleshooting

The following table lists and describes the recommended solution for each response status code:

Error Troubleshooting options
DataStructureRequestsOverLimit / PartitionRequestsOverLimit
  • Add a local cache by saving information to another variable and rechecking after a certain time interval, such as 30 seconds.
  • Use the **Request Count by Status** chart to verify that you are receiving more **Success** responses than **NoItemFounds**. Limit the amount of times you hit [`MemoryStoreService`](/docs/memorystoreservice) with a failed request.
  • Implement a short delay between requests.
  • Follow the [best practices](#best-practices), including:
    • Sharding your data structures if you receive a significant amount of **DataStructureRequestsOverLimit**/**PartitionRequestsOverLimit** responses.
    • Sharding your hash map keys if you receive a significant amount of **PartitionRequestsOverLimit** responses on hash map calls.
    • Reducing or batching calls to specific data structures or hash map keys if you see **PartitionRequestsOverLimit** responses.
    • Implement an exponential backoff for finding a reasonable rate of requests to send.
TotalRequestsOverLimit
DataStructureItemsOverLimit
  • Apply [best practices](#best-practices) on reducing the memory size.
DataStructureMemoryOverLimit
TotalMemoryOverLimit
DataUpdateConflict
  • Implement a short delay between requests to avoid multiple requests updating the same key at the same time.
  • For sorted maps, use the callback function on the [`MemoryStoreSortedMap:UpdateAsync()`](/docs/memorystoresortedmap#memorystoresortedmap-updateasync) method to abort a request after a certain number of attempts, as the following code sample shows:
  • ```lua title="Example of Aborting Request" local MemoryStoreService = game:GetService("MemoryStoreService") local map = MemoryStoreService:GetSortedMap("AuctionItems")
             function placeBid(itemKey, bidAmount)
                 map:UpdateAsync(itemKey, function(item)
                     item = item or { highestBid = 0 }
                     if item.highestBid < bidAmount then
                         item.highestBid = bidAmount
                         return item
                     end
                     print("item is "..item.highestBid)
                     return nil
                 end, 1000)
             end
    
             placeBid("MyItem", 50)
             placeBid("MyItem", 40)
             print("done")
             ```
          <li>Investigate to see if you're calling [`MemoryStoreService`](/docs/memorystoreservice) efficiently to avoid conflicts. Ideally, you shouldn't over-send requests.</li>
          <li>Consistently remove items once they are read using the [`MemoryStoreQueue:RemoveAsync()`](/docs/memorystorequeue#memorystorequeue-removeasync) method for queues and [`MemoryStoreSortedMap:RemoveAsync()`](/docs/memorystoresortedmap#memorystoresortedmap-removeasync) for sorted maps.</li>
        </ul>
      </td>
    </tr>
    <tr>
      <td>Internal Error</td>
      <td>
        <ul>
          <li>Check the <a href="https://status.roblox.com/pages/59db90dbcdeb2f04dadcf16d">Roblox status page</a>.</li>
          <li>File a <a href="https://devforum.roblox.com/t/how-to-post-a-bug-report/24388">bug report</a> describing the issue with your game's Universe ID.</li>
        </ul>
      </td>
    </tr>
    <tr>
      <td>InvalidRequest</td>
      <td>
        <ul>
          <li>Make sure that you include correct and valid parameters in your request. Examples of invalid parameters include:</li>
          <ul>
            <li>An empty string</li>
            <li>A string that exceeds the length limit</li>
          </ul>
        </ul>
      </td>
    </tr>
    <tr>
      <td>ItemValueSizeTooLarge</td>
      <td>
        <ul>
          <li>Shard or split the item value into multiple keys.</li>
          <ul>
            <li>To organize grouped keys, sort them alphabetically by adding a `prefix` to the key.</li>
          </ul>
          <li>Encoding or compressing stored values.</li>
        </ul>
      </td>
    </tr>

Test and debug in Studio

The data in MemoryStoreService is isolated between Studio and production, so changing the data in Studio doesn't affect production behavior. This means that your API calls from Studio don't access production data, allowing you to safely test memory stores and new features before going to production.

Studio testing has the same limits and quotas as production. For quotas calculated based on the number of users, the resulting quota can be very small since you are the only user for Studio testing. When testing from Studio, you might also notice slightly higher latency and elevated error rates compared to usage in production due to some additional checks that are performed to verify access and permissions.

For information on how to debug a memory store on live games or when testing in studio, use Developer Console.