Store
Every call on the object Ledger.New returns.
Look up any call on a store.
local Ok, Why, Info = Bank:Edit(Key, { Kind = "AddGold", Amount = Amount })
if not Ok then
warn("add failed:", Why)
end
local State, Why = Bank:Peek(Key, 30) -- up to 30 s old is fineA store holds one name and every key under it. A key is a string, or a Player or UserId in a player store. Most calls work on any store. The player calls (Load and friends) work on player stores only, and throw on a string store.
A bad call throws before anything is sent: a bad key, a wrong option name, or a call made from inside a reducer. Answers are never thrown. See Answers.
An op kind that isn't in your Ops type doesn't throw. Your reducer gets it, returns nil, and the call answers Refused. With --!strict, Studio's type check catches it before you run the game.
Changing data
| Call | What it does |
|---|---|
Edit(Key, Op, Options?) | One change, saved before it answers. |
Reset(Key, Options?) | Replaces a key's data with the Default or a state you give. |
Erase(Key, Options?) | Removes a key. Erasable stores only. |
Bump(Total, Amount, Options?) | Adds to a total. |
Leg(Key, Op, Options?) | Builds one entry for Ledger.Tx. Sends nothing. |
Edit
Options is { MustExist?, Id?, IdAt? }. MustExist overrides the store's setting for this call. A string Id needs IdAt too. Edit is safe on a key a player session holds.
| Answer | Means | Do |
|---|---|---|
true | Saved. | Done. |
Refused or Full | Your reducer said no, or the data would pass the size cap. Info.State is the data it judged. | Final for this name. Do not resend. |
Missing | MustExist and the key is not there. | Create it first, or turn MustExist off. |
Spent, Expired, NoRoom | The name is used up, forgotten, or the key's name list is full. | See Making a change happen once. |
Busy or Closed | Nothing was sent. | Busy: send the same call again later. Closed: stop. |
Behind or Unreadable | A newer build owns the key, or the key cannot be read. | Stop writing it from this build. |
Unresolved | Not known yet. Info.Outcome is known later. | A named Edit: send it again, same name. An unnamed one is still being sent: do not resend. |
Two things you may see:
Busyright after you resend a named Edit. AfterUnresolved, Ledger keeps sending the first one itself, and it won't send the same name twice at once. ExpectBusyfor a moment, then send it again. The name makes sure it only counts once.- An Edit that waits. If a trade is still being finished on the key, the Edit waits for it. When the server running that trade has gone down, finishing it takes longer, and the Edit answers
Unresolvedafter 30 seconds rather thanBusy. Ledger keeps sending it after that.
An op kind your reducer doesn't handle is refused. Studio's type check catches a kind that isn't in Ops before you run the game.
Reset and Erase
local Name = Ledger.Id()
if Name then
local Ok, Result = Bank:Erase(Key, { Id = Name })
endOptions for both is { Id } with a Ledger.Id() name, or nothing. A string Id throws, and so does IdAt beside it. Reset also takes State, a complete state in the newest shape. Make the name just before the call: a server answers Expired for its own name once it is 6 minutes old.
| Answer | Means | Do |
|---|---|---|
true, Cut | Done. Cut.Losses lists the balances that were erased, newest last. | Log Cut.Losses now. After an Erase this is the only time you get it. After a Reset, Store:Losses keeps them for 7 days. |
true, no Losses | A repeat of the same name, already done. Also what Erase with a Ledger.Id() name answers for a key that was never saved. | Done. |
Missing | Nothing to reset or erase. Erase with no name answers this for a key that was never saved. | Done: the key is gone. |
Expired | The name is too old. Nothing was sent. | Make a new name. |
Spent | The name was used for something else. | Make a new name. |
Busy or Closed | Nothing was sent. | Send again with the same name. |
Unresolved | Not known yet. | Send again with the same name, soon. |
Behind, Unreadable, Full | A newer build owns the key, it cannot be read, or the given State is too big. | See Answers. |
An Erase on a store with Erasable = false throws. See Deleting data.
Bump
task.spawn(function() -- Bump can wait up to a minute
Bank:Bump("GoldAdded", Amount)
end)The total must be declared in Totals. Amount is a whole number and may be negative. Unnamed bumps are best. After Unresolved, an unnamed bump is still being sent, so do not send it again. See Global counters.
Reading data
| Call | What it does |
|---|---|
Peek(Key, Freshness?) | The key's data. |
Inspect(Key) | The stored record, with its migration version. |
Total(Name, MaxAge?) | A declared total. |
Follow(Key) | An observer of the key's data. |
Stale() | An observer of keys this server wrote that its view is behind on. |
Keys(Prefix?) | Pages of every key, or of the keys that start with Prefix. |
Peek
Freshness is a number of seconds (0 to 2,592,000), or { Fresh = true }.
| Call | Gives |
|---|---|
Peek(Key) | The saved data, read now. |
Peek(Key, 0) | The same: a plain Peek. |
Peek(Key, 30) | A recent copy that all servers share. Cheaper for a key many servers read often. See Reading data. |
Peek(Key, { Fresh = true }) | The newest saved data. Costs a write, so use it rarely. |
| Answer | Means | Do |
|---|---|---|
| the data | The key's data at some moment. | Use it. |
nil, Missing | Never saved, or erased. It does not give the Default. | Use your own default. |
nil, Behind | A newer build owns the key. | Stop using this key. A plain Peek and { Fresh = true } answer this. Peek(Key, MaxAge) answers it too once the server has heard it, but a server that hasn't read the key since the update can answer the old copy once. |
nil, Unresolved | The read failed or took over 30 s. | Show "loading", try later. |
nil, Busy, Closed, Unreadable | See Answers. |
nil never means empty. See Reading data.
Total
Total(Name, MaxAge?) answers a number, or nil, Unresolved or nil, Behind. It's for totals. For a quantity, use Quantity(Name):Total(). A name that isn't in Totals or Quantities throws.
Keys
local Pages = Bank:Keys()
local Page, Why = Pages:Next()Next answers a list of up to 50 keys. nil, nil means the last page was read. nil, Unresolved means the fetch failed; the next Next tries the same page. Pages come in no set order.
Give a Prefix to list only the keys that start with it, such as Guilds:Keys("guild:"). It's for string stores; a player store throws.
Support calls
| Call | What it does |
|---|---|
DidApply(Key, Name, Options?) | Did a named op take effect? Options is { IdAt?, Probe? }. |
Losses(Key) | The balances that a Reset or Erase erased, kept for 7 days. |
Pending(Key) | Trade work that is not finished on a key. |
Resettle(Key) | Finishes that work now. Takes no options. |
Versions(Key) | Pages of the versions Roblox kept of the key. |
PeekVersion(Key, Version) | The key's data as one of those versions saved it. |
See Support tools. Pending answers Unresolved after 30 s on a hung read, as Peek does.
Versions
local Pages = Bank:Versions(Key)
local Page, Why = Pages:Next()Next answers a list of up to 1,024 versions, oldest first, and the same nil answers as Keys.
| Field | What it is |
|---|---|
Version | Roblox's id for the version. |
CreatedTime | When it was made, in milliseconds since 1970. Not a Ledger.Now() time. |
IsDeleted | true for the tombstone Roblox keeps when Ledger removes an erased key, a while after the Erase. Before that, the newest version is the erase's seal, which PeekVersion answers Missing. |
Roblox keeps one version for each hour the key was written, for 30 days. Ledger's own saves count too, so it isn't a list of your ops.
PeekVersion
local State, Why = Bank:PeekVersion(Key, Version)Reads one version: the key's data exactly as it was saved then. Migrations added since don't run on it, so it can be in an older shape than your Default. A trade still unfinished when the version was saved isn't in it. It never changes the key: to put old data back, send it as an op or a Reset.
| Answer | Means |
|---|---|
| the data | The key's data as that version saved it. |
nil, Missing | No such version, or the key was erased then. |
nil, Behind | A newer version of Ledger saved it. |
nil, Unreadable | Ledger didn't write it. |
nil, Unresolved | The read failed. Try again. |
nil, Closed | The server is shutting down. |
Quantities
Quantity(Name) returns the quantity object. It throws if the name is not in Quantities.
Players
| Call | What it does |
|---|---|
Load(Player) | Loads the player's session. Yields. Answers the session, or nil, Reason. |
Unload(Player) | Saves and ends the session. Does nothing if there is none. |
Get(Player) | The session, or nil. |
Expect(Player) | The session, or throws. |
IsLoaded(Player) | true if a session is loaded. |
WaitForLoaded(Player) | Waits for a load that has started. |
Read(Player) | The session's data, or nil. |
Load answers nil, nil if the player left. Otherwise the reason is Unresolved, Behind, Busy or Unreadable. A player with no saved data is never a failure: Load starts them from Default, even with MustExist = true. Then OnLoadFailed runs, and the player is kicked unless Kick = false.
Destroy
Destroy() ends every session and observer on the store. The store takes no more calls, and its name cannot be opened again on this server. You will rarely need it.
Next: Session