
Roblox Data
- 49 installs
- 10 repo stars
- Updated May 27, 2026
- stackfox-labs/luau-skills
Helps with ai & agent building tasks.
About
roblox-data is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- roblox-data
- AI & Agent Building
- AI-coding skill
Roblox Data by the numbers
- 49 all-time installs (skills.sh)
- +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #7,329 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/stackfox-labs/luau-skills --skill roblox-dataAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 49 |
|---|---|
| repo stars | ★ 10 |
| Last updated | May 27, 2026 |
| Repository | stackfox-labs/luau-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
roblox-data
When to Use
Use this skill when the task is mainly about Roblox data durability, shared cross-server state, or quota-aware coordination:
- Designing persistent player saves, global config-like records, or other
DataStoreServiceusage. - Choosing between standard data stores and ordered data stores.
- Structuring save payloads, schema versions, migrations, and metadata.
- Deciding when to use
SetAsync(),UpdateAsync(),IncrementAsync(), or version APIs. - Designing ephemeral cross-server systems with
MemoryStoreService. - Choosing between memory store queues, sorted maps, and hash maps.
- Coordinating multiple servers with
MessagingService. - Handling throttling, request budgets, retries, backoff, contention, and observability.
- Reasoning about stale reads, cache behavior, idempotency, and multi-server correctness.
Do not use this skill when the task is mainly about:
- Remote-event security, client-to-server validation, or general gameplay networking.
- OAuth flows, API key setup, or general Open Cloud authentication.
- Broad engine API lookup outside data services.
Decision Rules
- Use standard data stores for durable cross-session data that can be represented as numbers, strings, booleans, tables, or buffers.
- Use ordered data stores only when the stored value is numeric and the main requirement is persistent ranking or sorted retrieval.
- Prefer storing one related object per durable key instead of scattering related fields across many durable keys.
- Prefer
UpdateAsync()when multiple servers might write the same key or when the new value depends on the current value. - Use memory stores for shared data that is frequent, temporary, or coordination-oriented and can expire.
- Use a memory store hash map for keyed lookups and high fan-out across many keys.
- Use a memory store sorted map when ordering matters or when you need range reads by sort key.
- Use a memory store queue for ordered work processing, matchmaking queues, or claim-and-remove workflows.
- Use
MessagingServicefor short-lived broadcast signals, fan-out notifications, or wake-up coordination, not as the durable system of record. - If the task is mostly about remotes, replication to clients, or trust boundaries, hand off to
roblox-networking. - If the task is mostly about general runtime structure or script placement, hand off to
roblox-core. - If the task is mostly about member lookup, signatures, or class discovery, hand off to
roblox-api. - If a request mixes in out-of-scope material, answer only the data-service portion and exclude the rest.
Instructions
1. Classify the state before choosing a service:
- Durable across sessions.
- Temporary but cross-server.
- Broadcast-only coordination.
- Numeric ranking versus arbitrary structured data.
2. Choose the narrowest primitive that fits:
- Standard data store for durable objects.
- Ordered data store for durable numeric rankings.
- Hash map for keyed ephemeral state.
- Sorted map for ordered ephemeral state.
- Queue for claim-and-process work items.
- Messaging for notifications that can be regenerated from other state.
3. For persistent saves, define a stable schema:
- Keep one self-contained object per key when possible.
- Include a schema version field inside the value.
- Reserve migrations for load time or first write after load.
- Keep keys, scopes, and store names short and predictable.
4. Design writes for concurrency:
- Prefer
UpdateAsync()for contested keys. - Make callbacks deterministic and non-yielding.
- Return
nilto abort invalid updates. - Preserve existing metadata and user IDs when you do not intend to clear them.
5. Design reads with cache behavior in mind:
- Treat
GetAsync()as locally cached for a short window. - Use uncached reads only when freshness matters enough to justify extra budget use.
- Avoid reading immediately after writing from a different server unless the design accounts for staleness.
6. Treat quotas as part of the design:
- Check request budgets before bursty durable writes.
- Batch related durable data into one object where it improves atomicity and budget use.
- Keep memory-store TTLs as short as the use case allows.
- Remove queue and sorted-map items promptly after processing.
7. Design for retries and failure:
- Wrap network calls in
pcall(). - Retry transient failures with exponential backoff.
- Add jitter or spreading when many servers may retry together.
- Make retryable operations idempotent whenever possible.
8. Use observability to close the loop:
- Watch request counts, throttles, and quota usage for data stores.
- Watch memory usage, request-unit usage, and throttle statuses for memory stores.
- Use dashboards to confirm whether the bottleneck is global quota, per-key contention, or hot partitions.
9. Use messaging as a coordination layer, not storage:
- Publish compact events.
- Re-read or update authoritative state in data or memory stores as needed.
- Assume messages can be delayed or duplicated and make handlers safe.
10. Keep guidance inside scope:
- Focus on persistence, ephemeral shared state, quotas, and concurrency.
- Do not drift into remote security, gameplay networking, or auth flows.
Using References
- Open
references/data-stores-guides.mdfor standard versus ordered data stores, core CRUD patterns, metadata, serialization, and save-shape decisions. - Open
references/data-store-best-practices.mdfor durable schema layout, key organization, storage hygiene, and cleanup strategy. - Open
references/versioning-listing-caching-limits-and-observability.mdfor version history, prefix listing, cache behavior, limits, request budgets, throttling, and dashboards. - Open
references/memory-stores-guides.mdfor choosing between queues, sorted maps, and hash maps and for the core API patterns of each. - Open
references/memory-store-best-practices-limits-and-observability.mdfor TTL strategy, sharding, partition pressure, request-unit budgeting, contention handling, and dashboards. - Open
references/cross-server-messaging.mdfor topic design, publish-subscribe flow, and coordination patterns that pair messaging with durable or ephemeral state. - Open
references/data-stores-vs-memory-stores-comparison.mdwhen the first decision is which service class should own the data.
Checklist
- The state is classified as durable, ephemeral cross-server, or broadcast-only.
- The chosen service matches the durability and ordering requirements.
- Persistent keys use a stable schema with an explicit version field.
- Durable writes use
UpdateAsync()when contention is possible. - Ordered data stores are only used for numeric ranking data.
- Cache behavior and stale-read risk are accounted for.
- Request budgets, quotas, and throttling behavior are part of the design.
- Memory-store TTLs are intentionally short and cleanup paths are explicit.
- Queue items are removed after processing and sorted-map or hash-map items are pruned when stale.
- Messaging is used for coordination, not as the system of record.
- Retry logic uses
pcall()plus backoff rather than tight loops. - No remote security, OAuth, or general API-lookup material is included.
Common Mistakes
- Using
SetAsync()on hot keys that multiple servers can write concurrently. - Splitting one durable player profile across many unrelated keys without a strong reason.
- Using ordered data stores for structured blobs or metadata-heavy records.
- Forgetting that
GetAsync()can return a cached value for a few seconds. - Treating memory stores as durable storage.
- Using a queue when random keyed access or scans would fit a hash map better.
- Putting all hash-map traffic on one hot key and then hitting partition throttles.
- Keeping long TTLs on temporary memory-store items and filling quota with stale data.
- Using
MessagingServiceas the only source of truth for recoverable state. - Retrying immediately on throttles or conflicts and causing coordinated retry storms.
Examples
Choose the right service
- Player profile save: standard data store with one object per player key.
- All-time coins leaderboard: ordered data store keyed by player identifier with numeric values.
- Matchmaking pool: memory store queue.
- Cross-server auction board with ranking: memory store sorted map.
- Shared ephemeral room registry keyed by server id: memory store hash map.
- Force a cache refresh workflow across servers: message plus data-store or memory-store re-read.
Use UpdateAsync() for contested durable saves
local DataStoreService = game:GetService("DataStoreService")
local profileStore = DataStoreService:GetDataStore("PlayerProfiles")
local function saveCoins(userId, delta)
return profileStore:UpdateAsync(("player/%d"):format(userId), function(current, keyInfo)
current = current or {schemaVersion = 1, coins = 0}
current.coins += delta
return current, keyInfo:GetUserIds(), keyInfo:GetMetadata()
end)
endUse messaging to wake workers, not to hold state
-- Publish: "queue has work"
-- Receiver: read the queue or map, then process authoritative state there.Cross-Server Messaging
Key Concepts
MessagingServicebroadcasts short messages across servers through named topics.- It is a coordination tool, not durable storage.
- Topic subscribers react to events; durable or ephemeral state should live elsewhere.
- Messages should be small, self-describing, and safe to process more than once.
Rules
- Use
SubscribeAsync()to register a topic handler and keep the returned connection so you can disconnect it when appropriate. - Use
PublishAsync()for notifications, invalidation signals, and wake-up events. - Keep topic names short and stable.
- Keep message payloads compact and serializable.
- Assume message delivery timing is variable; re-check authoritative state before acting on a message that changes important data.
- Design handlers to tolerate duplicates or races.
- Do not use messaging as the sole recovery path for durable workflows.
Patterns
Cache invalidation
1. Write the authoritative state to a data store or memory store. 2. Publish a topic like profile-invalidated. 3. Receivers re-read or refresh their local cache.
Queue wake-up
1. Add work to a memory-store queue. 2. Publish a topic like jobs-available. 3. Workers react by reading the queue.
Cross-server announcement
- Publish a simple payload with event type and timestamp.
- Receivers fan it into local presentation or server-side handling.
Examples
Subscribe and react
local MessagingService = game:GetService("MessagingService")
local connection = MessagingService:SubscribeAsync("jobs-available", function(message)
print(message.Data)
-- Re-check queue or map here.
end)Publish after state change
local MessagingService = game:GetService("MessagingService")
MessagingService:PublishAsync("profile-invalidated", {
key = "player/12345/profile",
reason = "save-complete",
})Data Store Best Practices
Key Concepts
- Durable storage design is mostly about atomicity, key layout, and storage hygiene.
- Related data usually belongs in one value object rather than many keys.
- Prefixes are the preferred way to organize new key spaces.
- Memory stores are better for caches and temporary coordination data.
Rules
- Create fewer data stores and organize within them by keys or prefixes.
- Keep related user data together when the fields must move in lockstep.
- Use prefixes instead of new scopes for new systems unless legacy scope usage already exists.
- Delete test data and temporary event data instead of leaving permanent clutter.
- Prefer deleting by key over proliferating whole test data stores.
Patterns
Prefix-based key layout
player/12345/profileplayer/12345/loadout/1guild/9001/config
Benefits:
- Easy
ListKeysAsync()filtering. - Predictable cleanup and migration targeting.
- Less reliance on scopes.
Version-inside-value
{
schemaVersion = 2,
stats = {...},
items = {...},
}- Load path checks
schemaVersion. - Migration can happen in memory, then be written back once.
Separate permanent from temporary
- Durable unlocks or inventory: standard data store.
- Temporary cache, lock, queue, or match state: memory store.
Examples
Keep one profile object per player
Bad pattern:
coins/<userId>inventory/<userId>settings/<userId>
Better pattern:
{
schemaVersion = 4,
coins = 90,
inventory = {"bow"},
settings = {music = false},
}Use prefixes for profile variants
player/12345/profile/defaultplayer/12345/profile/mageplayer/12345/profile/tank
Data Stores Guides
Key Concepts
DataStoreServiceis for durable cross-session data.- Standard data stores hold numbers, strings, booleans, tables, and buffers.
- Ordered data stores hold numeric values and support sorted retrieval.
- Standard data stores support metadata and version history. Ordered data stores do not.
- Data-store calls are networked and should be wrapped in
pcall().
Rules
- Access data stores from server code only.
- Prefer one self-contained object per durable key when the fields should stay in sync.
- Use
UpdateAsync()when the next value depends on the current value or multiple servers might write the same key. - Use
SetAsync()only when blind overwrite is acceptable. - Keep store names, scopes, and keys within platform limits.
- Store only serializable Luau data. Do not store
nan,inf, or unsupported userdata.
Patterns
Durable profile object
- Key:
player/<userId> - Value:
{
schemaVersion = 3,
coins = 1250,
inventory = {"sword", "potion"},
settings = {
music = true,
sensitivity = 0.8,
},
}- Benefit: one read and one write keep related fields consistent.
Ordered leaderboard split from profile
- Standard store keeps the full profile.
- Ordered store keeps only the ranked numeric metric.
- Benefit: rich durable data stays in the standard store while the leaderboard stays queryable.
Metadata-aware save
- Use metadata for lightweight tags, provenance, or migration notes.
- Preserve existing metadata when the write is not meant to clear it.
Examples
Standard store write with update semantics
local DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("PlayerProfiles")
local function awardCurrency(userId, amount)
return store:UpdateAsync(("player/%d"):format(userId), function(current, keyInfo)
current = current or {schemaVersion = 1, currency = 0}
current.currency += amount
return current, keyInfo:GetUserIds(), keyInfo:GetMetadata()
end)
endOrdered store for persistent ranking
local DataStoreService = game:GetService("DataStoreService")
local leaderboard = DataStoreService:GetOrderedDataStore("CoinsLeaderboard")
local function setCoins(userId, coins)
return leaderboard:SetAsync(("player/%d"):format(userId), coins)
endData Stores vs Memory Stores Comparison
Key Concepts
- Data stores are durable. Memory stores are temporary.
- Both are cross-server and server-only.
- Data stores are the system of record for progress and permanent state.
- Memory stores are for coordination, fast shared state, caches, and expiring systems.
Rules
- Use a data store when the value must survive player leaves, server shutdowns, and long time spans.
- Use a memory store when the value can expire, be rebuilt, or is mainly about coordination.
- Use an ordered data store only for permanent numeric ranking.
- Use a memory store sorted map only for temporary ordering.
- Do not store secrets, auth tokens, or unrelated cloud credentials in either design discussion here.
Comparison
Standard data store
- Durable across sessions.
- Supports structured values.
- Supports metadata and version history.
- Best for player profiles, durable inventories, and permanent settings.
Ordered data store
- Durable across sessions.
- Numeric values only.
- Supports sorted retrieval.
- Best for all-time persistent leaderboards.
Memory store
- Up to 45-day lifetime.
- Lower latency and higher throughput for shared temporary state.
- Best for matchmaking, temporary leaderboards, server registries, locks, queues, and caches.
Patterns
Durable plus ephemeral pair
- Data store holds the permanent profile.
- Memory store hash map mirrors a short-lived cache for active sessions.
- Messaging invalidates or refreshes cache entries when writes occur.
Durable ranking plus temporary ranking
- Ordered data store for all-time score.
- Memory store sorted map for daily or weekly scoreboards.
Examples
Choose by requirement
- "Keep player inventory forever": standard data store.
- "Show top 100 all-time coins": ordered data store.
- "Track active match lobbies for the next 60 seconds": memory store hash map.
- "Process queued match requests across servers": memory store queue.
- "Broadcast that a new job is ready": messaging plus queue, not messaging alone.
Memory Store Best Practices, Limits, and Observability
Key Concepts
- Memory-store design is dominated by TTL, hot partitions, request units, and contention.
- Sorted maps and queues sit on a single partition each.
- Hash maps are automatically spread across partitions, but hot keys can still throttle.
- Request and memory quotas are experience-wide, not per server.
Rules
- Remove processed queue items immediately.
- Remove stale sorted-map and hash-map entries instead of leaving long-lived clutter.
- Set the shortest practical TTL.
- Shard sorted maps or queues when one structure becomes a hotspot.
- Split hot hash-map traffic across multiple keys when one key is overloaded.
- Use exponential backoff on conflicts and throttles.
- Keep values small; each memory-store item value is limited to 32 KB.
Limits
Experience-wide quotas
- Memory quota:
64 KB + 1.2 KB * concurrent users. - Request-unit quota:
1000 + 120 * concurrent usersper minute.
Structure and item limits
- Sorted map or queue: 1,000,000 items max.
- Sorted map or queue: 100 MB total size max.
- Hash map key size: 128 characters max.
- Sorted map key size: 128 characters max.
- Sorted map sort key size: 128 characters max.
- Value size: 32 KB max.
- Expiration time: 0 to 3,888,000 seconds.
Request-unit notes
MemoryStoreSortedMap:GetRangeAsync()costs based on items returned.MemoryStoreQueue:ReadAsync()costs based on items returned and wait time.MemoryStoreHashMap:UpdateAsync()costs at least two units.MemoryStoreHashMap:ListItemsAsync()costs partitions scanned plus items returned.
Patterns
Shard a sorted map
- Split by prefix or bucket range.
- Example buckets:
A-G,H-N,O-T,U-Z. - Route each key to a bucket helper.
Shard a queue
- Use several queues and rotate reads and writes across them.
- Readers merge results from all shards.
De-hotspot a hash map
- Avoid one
metadatakey that every server reads. - Store separate keys like
metadata/playerCount,metadata/mode,metadata/season. - If one value is still too hot, replicate it across several equivalent keys and distribute reads.
Observability
- Use Memory Stores dashboard charts for memory usage, request-unit usage, request count by API, and request count by status.
- Watch for
PartitionRequestsOverLimit,DataStructureRequestsOverLimit,TotalRequestsOverLimit, andDataUpdateConflict. - Warning and critical alerts indicate sustained pressure, not isolated blips.
Examples
Conflict-safe sorted-map update
local board = game:GetService("MemoryStoreService"):GetSortedMap("AuctionBoard")
board:UpdateAsync("item/42", function(current, sortKey)
current = current or {highestBid = 0}
if current.highestBid >= 500 then
return nil
end
current.highestBid = 500
return current, 500
end, 120)Backoff sketch
local delaySeconds = 1
for attempt = 1, 5 do
local ok, result = pcall(operation)
if ok then
return result
end
task.wait(delaySeconds)
delaySeconds *= 2
endMemory Stores Guides
Key Concepts
MemoryStoreServiceis for temporary, cross-server, high-frequency state.- Data expires by TTL, up to 45 days.
- Choose the data structure by access pattern, not by habit.
- Hash maps favor keyed access and broad partitioning.
- Sorted maps favor ordering and range reads.
- Queues favor claim-and-process workflows with invisibility timeout.
Rules
- Use memory stores only for data that can expire or be rebuilt.
- Prefer hash maps if you do not need ordering or queue semantics.
- Prefer sorted maps for leaderboards, ranked boards, and order-dependent scans.
- Prefer queues when workers claim items and remove them after processing.
- Wrap calls in
pcall()and expect transient failures or contention. - Set explicit expiration times instead of leaning on the default long lifetime.
Patterns
Hash map
Use when:
- You read by known key.
- You need many keys.
- You want better partition spreading.
Examples:
- Server registry keyed by
server/<jobId>. - Shared temporary inventory keyed by item id.
- Cross-server cache of durable records.
Sorted map
Use when:
- You need ordering by sort key.
- You need
GetRangeAsync(). - You are building global daily leaderboards, auctions, or ranked listings.
Examples:
leaderboard/<userId>with score as sort key.- Auction entries sorted by bid or expiration score.
Queue
Use when:
- Work should be processed in FIFO or priority order.
- Consumers should claim items temporarily, then remove them on success.
Examples:
- Matchmaking pool.
- Retry work queue for background processing.
Examples
Hash map for server presence
local MemoryStoreService = game:GetService("MemoryStoreService")
local servers = MemoryStoreService:GetHashMap("ActiveServers")
servers:SetAsync(game.JobId, {
playerCount = #game:GetService("Players"):GetPlayers(),
region = "eu-west",
}, 60)Sorted map for temporary ranking
local board = MemoryStoreService:GetSortedMap("DailyDamage")
board:SetAsync("player/12345", {damage = 9000}, 86400, 9000)Queue for workers
local queue = MemoryStoreService:GetQueue("Matchmaking", 30)
queue:AddAsync({userId = 12345, rating = 1500}, 60)Versioning, Listing, Caching, Limits, and Observability
Key Concepts
- Standard data stores automatically create version history on the first write to a key in each UTC hour.
- Older overwritten versions expire 30 days after a newer write replaces them. The latest version does not expire.
ListVersionsAsync(),GetVersionAsync(), andRemoveVersionAsync()operate on version history.GetAsync()uses a local cache for about four seconds by default.- Listing and version APIs fetch current backend state and do not use the
GetAsync()cache. UpdateAsync()consumes both read and write budget.
Rules
- Use versioning as the rollback path for standard data stores, not extra backup keys.
- Prefer prefixes for organization and for
ListKeysAsync()filtering. - Use uncached reads only when freshness matters more than budget conservation.
- Check
GetRequestBudgetForRequestType()before bursty durable work. - Treat throttles and internal errors as retryable with backoff.
- Do not rely on Studio data-store access against live data unless that is intentionally configured.
Patterns
Restore near a known incident time
1. List versions for the key in descending order up to the target timestamp. 2. Load the closest valid version. 3. Overwrite the live key with that version.
Prefix listing
player/guild/season/2026/
Use list queries to target a family of keys instead of inventing many store names.
Cache-aware read strategy
- Normal read path: cached
GetAsync()for lower budget cost. - Freshness-critical path:
GetAsync()with caching disabled. - Cross-server write-read path: avoid assuming another server sees the new value immediately.
Limits
Data shape
- Data store name: 50 characters max.
- Key name: 50 characters max.
- Scope: 50 characters max.
- Value size: 4,194,304 bytes per key after serialization.
- Metadata total size: 300 characters across key-value pairs.
Current server request budgets
- Standard get:
60 + players * 10per minute. - Standard set/write family:
60 + players * 10per minute. - Ordered sorted listing:
5 + players * 2per minute. - Ordered set/write family:
30 + players * 5per minute. - List and version families are smaller and should be treated as expensive.
Throughput
- Per-key read throughput: 25 MB per minute.
- Per-key write throughput: 4 MB per minute.
- Throughput is rounded up to the next kilobyte per request.
Observability
- Use the Data Stores dashboard for storage usage, request count by API, request count by status, and quota-usage charts.
- Separate standard versus ordered views when debugging.
- Use dashboard status distributions to tell apart normal load, throttling, and backend errors.
Examples
Disable cache for a freshness-critical read
local options = Instance.new("DataStoreGetOptions")
options.UseCache = false
local value = store:GetAsync(key, options)Budget gate before a write burst
local budget = DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.UpdateAsync)
if budget <= 0 then
return false
end