Ledger
Reference

Ledger

The top level module.

local Ledger = require(ServerStorage.Ledger)

Ledger.New

Ledger.New<D>(Options: Config<D>) -> Store<D>

Builds a store. Throws on anything wrong with the options, at build time, rather than letting it misbehave later.

OptionType
NamestringRequired. The datastore name, 1 to 47 characters.
Reducer(State, Op) -> State?Required. See Writing a reducer.
DefaultDRequired. The fresh state table.
Balancestring?Names a number field for transfers.
Migrations{ Migration }?See Migrations.
Keys"Player" | "String"Defaults to "Player".
OnLoadFailed((Player, Reason) -> boolean)?What to do when a load fails. See below.

The name caps at 47 rather than the datastore's 50 because Ledger also creates <Name>_Tx for transaction markers.

Default can't declare any key starting with _, and it has to be storable: numbers, strings, booleans, tables and buffers.

Two stores can't share a name in one server. Call Store:Destroy() first if you need to rebuild one.

OnLoadFailed

A load that fails kicks the player with "Your data failed to load, please rejoin". OnLoadFailed takes that decision instead. It gets the player and the reason, and returns whether it dealt with them. true and Ledger leaves them alone, false and it kicks.

local Store = Ledger.New({
	Name = "PlayerData",
	Reducer = Reducer,
	Default = { Gold = 0 },
	OnLoadFailed = function(Player: Player, Why: Ledger.Reason): boolean
		if Why == Ledger.Reason.Behind then
			-- this server is running an old build, send them to one that isn't
			TeleportService:Teleport(game.PlaceId, Player)
			return true
		end
		return false
	end
})

Behind means a newer build wrote the record, so a rejoin puts them on the same old server and fails again. Unresolved is usually a datastore outage, where a rejoin is the right answer.

It can yield, so a teleport works. Anything waiting in WaitForLoaded is released before it runs.

If it throws, Ledger warns and kicks. If it returns true and the player is still in the server with no data, Ledger warns about that too.

Only for Keys = "Player" stores. Passing it on a Keys = "String" store throws at build time.

Ledger.Reason

The ten reasons, as constants. See Reasons.

Ledger.Reason.Refused
Ledger.Reason.Busy
Ledger.Reason.Spent
Ledger.Reason.Held
Ledger.Reason.Unresolved
Ledger.Reason.Closed
Ledger.Reason.Backlog
Ledger.Reason.Full
Ledger.Reason.Invalid
Ledger.Reason.Behind

Ledger.UseMock

Ledger.UseMock(Options: { Players: number?, CCU: number?, Throttled: boolean? }?) -> Service

Points every store at an in memory datastore, including ones you built before the call.

Call it one time. Each call hands back a fresh empty service, and every store moves onto it, so a second call anywhere in your game makes everything written up to that point unreadable. Ledger warns if it catches you doing it while stores are live.

Option
Playersnumber?How many on this server. Defaults to 0. Sizes the per server budget.
CCUnumber?How many in the whole experience. Defaults to Players. Sizes the experience budget.
Throttledboolean?Defaults to true.

Both budgets apply at the same time. The smaller one stops you first, the same as a real server. See Testing.

Ledger.UseReal

Ledger.UseReal() -> ()

Back to the real DataStoreService. Only affects stores built after the call.

Ledger.UseClock

Ledger.UseClock(Reads: (() -> number)?, Sleeps: ((Seconds: number) -> ())?) -> ()

Swaps the function Ledger reads the time from. Pass nil to go back to os.time.

Sleeps swaps the wait Ledger polls on, and defaults to task.wait. There is one poll, in Session:CommitOp, where a commit waits on a transaction another server parked on the key. Its deadline is read off your clock.

Ledger.Sweep

Ledger.Sweep() -> ()

Forces a background maintenance pass right now: finish stranded transfers, settle stuck transaction legs, tidy up old markers. The sweeper does this on its own, so this is for tests and incidents.

Ledger.CloseAll

Ledger.CloseAll() -> ()

Shuts everything down. Stops the sweeper, tells every queue to skip ahead to the last write, saves every live session, and yields until it's all done.

Put this in BindToClose and nothing else.

game:BindToClose(function()
	Ledger.CloseAll()
end)

Exported types

Ledger.Store<D>
Ledger.Session<S>
Ledger.Op
Ledger.Reducer<S>
Ledger.Reason
Ledger.KeyLike
Ledger.KeysMode
Ledger.TxLeg
Ledger.HoldOptions
Ledger.Migration
Ledger.Config<D>
Ledger.HistoryEntry

See Types.

On this page