Ledger

Releases

What changed from v6, and what you have to change with it.

+ is new in v7, ~ exists in v6 and works differently now, - is gone, ! is something to know before you upgrade.

7.0.2

+ Store:Keys(Prefix) lists only the keys that start with Prefix, on a string store
+ Store:Versions(Key) lists the versions Roblox kept of a key
+ Store:PeekVersion(Key, Version) reads one of those versions, as it was saved

7.0.1

+ One op in a Ledger.Tx or Quantity:Take leg can be up to 4,096 bytes of terms, up from 512

The 512 byte cap was left over from an early design that stored a leg's op in its key.

7.0.0

Ledger 7 is rebuilt from scratch. It's cheaper, holds more, and every edge case we could find is tested.

What a 100k CCU game gets

v6v7
Trades a minute225,000900,000
Trades a day324 million1.3 billion
Trades a day through one guild bank or shop1,900590,000
Trades running on one key at once116
MemoryStore requests a minute spent on trades1.8 million0
Players in one trade429
Data per player2 MB4 MB
Limited item sales a minute, never oversoldnone900,000
Global counter adds a minutenoneunlimited, for 1.6% of your write budget

What's new

  • Balances. Number fields like gold or gems that you change with Credit and Debit ops, with Min and Max limits. They never hold up a trade.
  • Quantities. Limited items sold from every server at once, never oversold, with Take, Hold, Confirm, Close, Deposit, Gather and Withdraw.
  • Totals. Count things across the whole game, like total gold spent. Reading them is free most of the time.
  • Answers straight away. Every call answers (Ok, Result, Info) at once. No more :Wait().
  • Info.Outcome. When a save answers Unresolved, this tells you later whether it went through.
  • Six new answers. Unreadable, Expired, NoRoom, Missing, Short and SoldOut, each with one meaning. See Answers.
  • Strict types. Every op you send is checked against your Ops type before you press Play.
  • Safer deletes. Reset and Erase list the balances they wiped in Cut.Losses, and a retried call can never wipe twice.
  • Support tools. Store:Keys, Store:Losses and Store:Pending for looking into a player's data.
  • Session:Refresh. Show a player changes made by another server or a trade, straight away.
  • Fresh reads. Peek(Key, { Fresh = true }) reads the newest saved data.
  • Ledger.Now() and Ledger.BeforeClose(). A clock for named ops, and a hook that runs before shutdown saves.
  • No BindToClose. Ledger saves every player on shutdown by itself.

Every change

+ Ledger.Now() and Ledger.BeforeClose()
+ Mock: run a server on the Mock package's in-memory DataStore and MemoryStore
+ Balances: number fields like gold that Ledger changes for you, with limits, and that never hold up a trade
+ Store:Keys(), Store:Losses(), Store:Pending(), Session:Refresh()
+ Quantities: Take, Hold, Confirm, Close, Deposit, Gather, Withdraw
+ Cut.Losses lists what Reset and Erase destroyed
+ Info.Outcome tells you what became of an Unresolved write
+ Six answers: Unreadable, Expired, NoRoom, Missing, Short, SoldOut
+ Peek(Key, { Fresh = true }) reads the newest saved data
~ Every call answers (Ok, Result, Info), with no Future and no Wait. A read never means empty
~ 15 answers instead of 10, all checked by Ledger.Reason
~ Peek of a key that was never saved answers nil, Missing. It no longer gives the Default
~ Ops are named with Id and IdAt in the options (Edit, Commit, Ledger.Tx)
~ Ledger.Id() answers nil, Busy when it cannot make a name yet. Erase and Reset take one
~ Reset and Erase answer a Cut
~ Ledger.Tx, Bump, Total, Follow, Stale and Peek with MaxAge keep their names, with new answers
~ Ledger.New<<Data, Ops>> type-checks every op you send against your Ops type
~ TypeScript typings for roblox-ts, rebuilt for v7: @xoifail/ledger 7.0.0
- Held, and calls that return a Future
- Transfer, Reserve and Once. Use Ledger.Tx, quantities and named ops
- EditOp, CommitOp, Session:Compact, Ledger.Sweep, History, PeekVersion

! Data written by v6 may read Unreadable or Behind. Use new store names for v7
! Erase and Reset take only a Ledger.Id() name, or none. A string Id throws
! A server answers Expired for its own Ledger.Id() name once it is 6 minutes old. Make the name just before you use it
! For a quantity's counts use Store:Quantity(Name):Total(). Store:Total is for totals
! A Final quantity must declare Stock

Upgrading

Plan for a rewrite of the calls, not a version bump. Start with Getting started, then Answers and Common mistakes.

v7 does not read v5 or v6 data. Open v7 stores under new names, and copy each player's old data in on their first join, as in Changing your data.

Or use the AI skill to help. Install it with npx skills add XoifaiI/Ledger.

On this page