Skip to main content

Ephemeral stores and premades

MemoryStore is fast and shared by every server, but its data expires and its quota is shared by the whole experience. Geode wraps it with validation and retries, and builds the tools most games write by hand on top. Nothing here costs quota until you create it.

Wrappers​

Geode.Ephemeral gives hash maps, sorted maps and queues. Values are checked against an optional schema before they're written, and every call goes through the scheduler, so throttles are retried with backoff.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Geode = require(ReplicatedStorage.Packages.Geode)

local Lobbies = Geode.Ephemeral.hashMap("Lobbies", {
schema = Geode.t.struct({ host = Geode.t.integer(), open = Geode.t.boolean() }),
})

Lobbies:set("lobby_7", { host = 1001, open = true }, 300)

local lobby = Lobbies:get("lobby_7")
if lobby.ok then
print(lobby.value)
end

Premades​

PremadeWhat it gives you
Geode.Presence.new(options)Which server each player is on, kept alive with heartbeats
Geode.Mutex.new(name, options)A lock every server respects, with a lease and a fencing token
Geode.Counter.new(name, options)Counters every server can change atomically
Geode.RateLimiter.new(name, { limit, window })A limit shared by every server, counted in fixed windows
Geode.JobQueue.new(name, options)Jobs any server adds and one server at a time claims
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Geode = require(ReplicatedStorage.Packages.Geode)

local Bosses = Geode.Mutex.new("Bosses", { lease = 30 })
local acquired = Bosses:acquire("dragon", 5)
if acquired.ok and acquired.value then
local lock = acquired.value
print("spawning the dragon with token", lock.token)
lock:release()
end

local Kills = Geode.Counter.new("Kills")
Kills:increment("global", 1)

local Trades = Geode.RateLimiter.new("Trades", { limit = 5, window = 60 })
local allowed = Trades:allow("player_1001")
if allowed.ok and not allowed.value then
print("too many trades, try again in a minute")
end

local Rewards = Geode.JobQueue.new("Rewards")
Rewards:push({ userId = 1001, coins = 100 })
local claimed = Rewards:claim()
if claimed.ok and claimed.value then
print(#claimed.value.jobs, "jobs to grant")
claimed.value:ack()
end

A few choices keep quota low, and each one is visible:

  • A rate limiter counts in fixed windows with one atomic update per check, so a burst across a window edge can reach twice the limit
  • A lock's token never goes below the wall clock in milliseconds, so it keeps growing even after the stored item expires. Pass it along with writes so a holder whose lease ran out can be refused
  • A job that isn't acknowledged reappears once its invisibility timeout passes

Every premade reports estimatedCost(), the requests per minute it's spending right now.