Ledger
Reference

Session

One player's data, live on this server.

You get one from Store:Get, Store:Expect or Store:WaitForLoaded. Sessions only exist on player keyed stores.

Fields

LogSize

Session.LogSize: number

How many ops are in the stored log. Read only.

LogBytes

Session.LogBytes: number

Roughly how many bytes the stored ops plus the queued ones take. Read only.

Reading

Get

Session:Get() -> S

Live state. Every table in it is frozen, at every depth, so a mutation throws on the line that did it:

local State = Session:Get()
State.Gold = 99                --> attempt to modify a readonly table
State.Bag.Items[1] = "sword"   --> attempt to modify a readonly table

Observe

Session:Observe() -> Observer<S>

Fires on every change that goes through, including ones from another server that turned up when a transfer or transaction settled. Doesn't fire for a refused op.

Session:Observe():Subscribe(function(State)
	UpdateHud(Player, State)
end)

This is the session's own stream, not a copy, so Session:Observe() == Session:Observe(). Destroy on it removes Ledger's own subscribers too, so don't call it. See Observer for the chain methods and cleanup.

DidApply

Session:DidApply(Id: string) -> boolean

Whether a Once name ever applied on this key. Reads live state, so it doesn't yield.

Live state includes the ops that wait for the next save. Apply puts a name in that queue, so DidApply gives true before the op is written. It tells you that the name applied. It does not tell you that the name is saved. To be sure that it is saved, use Commit, or use Apply and then Flush.

Writing

Apply

Session:Apply(Kind: string, Fields: { [any]: any }?) -> (boolean, Reason?)

Instant and local. Runs the reducer, updates state, tells observers, queues the op for the next save. Never touches the datastore.

Use it for gameplay. See Apply and Commit.

Commit

Session:Commit(Kind: string, Fields: { [any]: any }?) -> Future<boolean, Reason?>

Pushes everything queued, appends the op, waits for the datastore, refolds, then answers. true means it's durable and every server will agree.

Use it for anything you can't take back.

CommitOp

Session:CommitOp(Op: Op) -> Future<boolean, Reason?>

Same as Commit but you build the op. Needs a string Id and a string Kind. A Once field, if you give one, has to be a non empty string.

Session:CommitOp({
	Id = HttpService:GenerateGUID(false),
	Kind = "ProductGrant",
	ProductId = 123456,
	Once = `receipt:{Receipt.PurchaseId}`,
}):Wait()

Id, Kind and OnceAt belong to Ledger. Passing them in Fields through Apply or Commit warns and gets overwritten.

Saving

Flush

Session:Flush() -> Future<boolean, Reason?>

Pushes queued ops without adding one. What autosave calls. false comes with a reason and the ops stay queued for next time.

Compact

Session:Compact() -> Future<boolean, Reason?>

Folds the log down into a fresh snapshot and drops the ops it absorbed. Autosave does this when the log gets long, so you rarely need it.

Release

Session:Release() -> Future<boolean, Reason?>

Marks the session closed and pushes what's left. Apply, Commit and CommitOp then answer Closed. Flush still works, it adds no op.

Use Store:Unload instead. Release closes the session and tells the store nothing, so IsLoaded stays true, Get still hands back the closed session, and the autosave timer keeps running. Unload does both halves.

On this page