Skip to main content

Session

This item is only intended to be used by the module's authors. Private

Manages the in-memory state and persistence lifecycle for a single data key within a Store. A Session represents an active, locked instance of a key's data, coordinating data loading, saving, updates, transactions, migrations, and cleanup.

Sessions are created internally by Store:load after acquiring a distributed lock via the Locks module. They handle:

  • Loading initial data by reading the main record, file shards, and pending transactions, applying migrations, or importing legacy data.
  • Providing access to the current data (Session:get).
  • Applying mutations (Session:update) safely within a noYield context and tracking changes.
  • Saving changes back to DataStores (Session:save, Session:updateRecord, Session:writeRecord), potentially sharding large data via the Files module.
  • Coordinating multi-key transactions (Store:tx interacts with Session state).
  • Automatically saving data periodically (Session:startAutosaving).
  • Cleaning up orphaned file shards in the background (Session:orphanFile).
  • Gracefully unloading data (Session:unload), ensuring final saves and lock release.

A Session is intrinsically tied to a Locks.LockHandle. If the lock is lost, the Session becomes 'closed' and unusable, triggering cleanup.

Types

Session<T>

type Session<T> = typeof(setmetatable({}:SessionProps<T>,{}:SessionImpl<T>))

Represents an active, locked session for a specific data key.

Functions

load

Session.load(
paramsLoadSessionParams<T>--

Parameters for loading the session.

) → Promise<Session<T>>--

Resolves with the new Session instance.

Static method to load data and create a Session. This is the main entry point called by Store:load.

Steps:

  1. Acquire the distributed lock for the key using Locks.acquireLock.
  2. If lock acquired, call the internal load helper function.
  3. If load successful and lock still held, create the Session instance using createSession.
  4. Set up lock loss handling (lockHandle.onLockLost) to close the session.
  5. Initiate background cleanup for any orphaned files found during load.
  6. Set the initial data in the session using mutateKey.
  7. Return the created Session.
  8. Ensure the lock is released if loading fails at any stage.

Errors

TypeDescription
"Lock was lost while loading key"
stringPropagates errors from `Locks.acquireLock` or internal `load`.

isSaved

Session:isSaved() → boolean--

True if there are no unsaved changes, false otherwise.

Checks if the session has any unsaved changes.

startAutosaving

Session:startAutosaving() → ()

Starts the background autosave loop for this session. Does nothing if already started or if the session is closed.

stopAutosaving

Session:stopAutosaving() → ()

Stops the background autosave loop if it's running.

unload

Session:unload() → Promise--

Resolves when the unload process is complete.

Initiates the graceful shutdown process for the session. Stops autosaving, queues a final save operation, and releases the lock.

get

Session:get() → Promise<T>--

Resolves immediately with the data.

Gets the current data for the session. Value returned is deep frozen to prevent modification.

update

Session:update(
transformFunction(dataT) → boolean--

Function to modify data. Must return true to commit, false to abort.

) → Promise<boolean>--

Resolves with true if changes were committed, false if aborted by the transform.

Applies updates to the session's data via a transform function. Ensures the transform runs without yielding and validates the result against the schema. Allows you to directly mutate the data in-place.

Errors

TypeDescription
"Session is closed"
"transformFunction failed: ..."If the transform function errors.
"transformFunction must return a boolean"
"Store:update schema validation failed: ..."If the modified data fails schema validation.

updateImmutable

Session:updateImmutable(
transformFunction(dataT) → T | false--

Function to modify data. Must return a new copy of the data with changes to commit changes, or false to abort.

) → Promise<boolean>--

Resolves with true if changes were committed, false if aborted by the transform.

Applies updates to the session's data via a transform function. Ensures the transform runs without yielding and validates the result against the schema. Requires the use of copy-on-write semantics in transformFunction, where the data is not directly mutated but instead a new copy is returned.

Errors

TypeDescription
"Session is closed"
"transformFunction failed: ..."If the transform function errors.
"transformFunction must return a boolean"
"Store:update schema validation failed: ..."If the modified data fails schema validation.

save

Session:save() → Promise--

Resolves when the save operation completes (or immediately if no changes).

Queues a save operation for the current session state if changes are pending. Uses the session's PromiseQueue to serialize saves.

Errors

TypeDescription
"Session is closed"
Show raw api
{
    "functions": [
        {
            "name": "load",
            "desc": "Internal helper function to perform the multi-stage loading process for a key.\nThis runs *after* the lock has been acquired.\n\nSteps:\n1. Get the main record from `recordStore`.\n2. Extract metadata (applied migrations, orphaned files).\n3. If a file reference exists, read the file data (potentially from shards via `Files.read`).\n4. If file data contains transaction info (`txInfo`), resolve the transaction state via `Transactions.readTx`.\n5. If no data found yet, attempt to import legacy data via `ctx.importLegacyData`.\n6. If still no data, use the `ctx.template` as the base.\n7. Apply any pending migrations via `Migrations.apply`.\n8. Return the final loaded data and metadata.",
            "params": [
                {
                    "name": "params",
                    "desc": "Parameters for loading.",
                    "lua_type": "LoadParams"
                }
            ],
            "returns": [
                {
                    "desc": "Promise resolving with the loaded data and metadata.",
                    "lua_type": "Promise<LoadResult>"
                }
            ],
            "function_type": "static",
            "private": true,
            "source": {
                "line": 228,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "createSession",
            "desc": "Internal factory function to create a new Session instance.\nInitializes the session state based on loaded data and acquired lock.",
            "params": [
                {
                    "name": "params",
                    "desc": "Parameters for session creation.",
                    "lua_type": "CreateSessionParams"
                }
            ],
            "returns": [
                {
                    "desc": "The newly created session instance.",
                    "lua_type": "Session<T>"
                }
            ],
            "function_type": "static",
            "private": true,
            "source": {
                "line": 413,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "load",
            "desc": "Static method to load data and create a Session. This is the main entry point\ncalled by `Store:load`.\n\nSteps:\n1. Acquire the distributed lock for the key using `Locks.acquireLock`.\n2. If lock acquired, call the internal `load` helper function.\n3. If load successful and lock still held, create the Session instance using `createSession`.\n4. Set up lock loss handling (`lockHandle.onLockLost`) to close the session.\n5. Initiate background cleanup for any orphaned files found during load.\n6. Set the initial data in the session using `mutateKey`.\n7. Return the created Session.\n8. Ensure the lock is released if loading fails at any stage.",
            "params": [
                {
                    "name": "params",
                    "desc": "Parameters for loading the session.",
                    "lua_type": "LoadSessionParams<T>"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with the new Session instance.",
                    "lua_type": "Promise<Session<T>>"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Lock was lost while loading key\"",
                    "desc": ""
                },
                {
                    "lua_type": "string",
                    "desc": "Propagates errors from `Locks.acquireLock` or internal `load`."
                }
            ],
            "source": {
                "line": 470,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "updateRecord",
            "desc": "Internal method to trigger a save operation if there are pending changes.\nThis is called by `Session:save` and `Session:unload`.",
            "params": [],
            "returns": [
                {
                    "desc": "Resolves when the save is complete (or immediately if no changes).",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "private": true,
            "source": {
                "line": 559,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "orphanFile",
            "desc": "Marks a file (usually an older, sharded file) for deletion and starts a\nbackground task to remove its shards from the `shardStore`.\n\nHandles DataStore budget limitations by waiting if the budget is too low.",
            "params": [
                {
                    "name": "file",
                    "desc": "The file metadata (must include `shard` and `count` if sharded).",
                    "lua_type": "Types.File"
                }
            ],
            "returns": [],
            "function_type": "method",
            "private": true,
            "source": {
                "line": 587,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "writeRecord",
            "desc": "Core internal method responsible for writing the current session data to DataStores.\n\nSteps:\n1. Write the data (as `txInfo`) to potentially multiple shards using `Files.write`.\n2. If sharding fails, mark the failed shards as orphaned and reject.\n3. If file write succeeds, check if the session lock is still held.\n4. If lock lost, mark the newly written file as orphaned and reject.\n5. Prepare the main record (`DataStoreRecord`) containing metadata (migrations,\n   new file reference, list of files now orphaned by this save).\n6. Write the main record to the `recordStore` using SetAsync.\n7. If record write succeeds:\n\ta. Mark the *previous* `currentFile` (if any) as orphaned using `orphanFile`.\n\tb. Update `self.currentFile` to the newly written file reference.\n\tc. Return the written record.\n8. If record write fails, mark the newly written file as orphaned and reject.",
            "params": [
                {
                    "name": "txInfo",
                    "desc": "The data payload to write (contains `committedData`).",
                    "lua_type": "Types.TxInfo"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with the written record on success.",
                    "lua_type": "Promise<Types.DataStoreRecord>"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"lock was lost while writing file\"",
                    "desc": ""
                },
                {
                    "lua_type": "string",
                    "desc": "Propagates errors from `Files.write` or `SetAsync`."
                }
            ],
            "private": true,
            "source": {
                "line": 679,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "isSaved",
            "desc": "Checks if the session has any unsaved changes.",
            "params": [],
            "returns": [
                {
                    "desc": "True if there are no unsaved changes, false otherwise.",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 762,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "setData",
            "desc": "Internal method to update the session's in-memory data state.\nMarks the session as having unsaved changes and updates the data.",
            "params": [
                {
                    "name": "data",
                    "desc": "The new data value.",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "method",
            "private": true,
            "source": {
                "line": 775,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "mutateKey",
            "desc": "Internal method called after data has been changed (either initially loaded or via `update`).\nUpdates the session state (`setData`) and triggers any configured `changedCallbacks`.",
            "params": [
                {
                    "name": "newData",
                    "desc": "The new data value.",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "method",
            "private": true,
            "source": {
                "line": 792,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "startAutosaving",
            "desc": "Starts the background autosave loop for this session.\nDoes nothing if already started or if the session is closed.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 810,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "stopAutosaving",
            "desc": "Stops the background autosave loop if it's running.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 862,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "unload",
            "desc": "Initiates the graceful shutdown process for the session.\nStops autosaving, queues a final save operation, and releases the lock.",
            "params": [],
            "returns": [
                {
                    "desc": "Resolves when the unload process is complete.",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 878,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "get",
            "desc": "Gets the current data for the session. Value returned is deep frozen to prevent modification.",
            "params": [],
            "returns": [
                {
                    "desc": "Resolves immediately with the data.",
                    "lua_type": "Promise<T>"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 921,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "update",
            "desc": "Applies updates to the session's data via a transform function.\nEnsures the transform runs without yielding and validates the result against the schema.\nAllows you to directly mutate the data in-place.",
            "params": [
                {
                    "name": "transformFunction",
                    "desc": "Function to modify data. Must return `true` to commit, `false` to abort.",
                    "lua_type": "(data: T) -> boolean"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with `true` if changes were committed, `false` if aborted by the transform.",
                    "lua_type": "Promise<boolean>"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Session is closed\"",
                    "desc": ""
                },
                {
                    "lua_type": "\"transformFunction failed: ...\"",
                    "desc": "If the transform function errors."
                },
                {
                    "lua_type": "\"transformFunction must return a boolean\"",
                    "desc": ""
                },
                {
                    "lua_type": "\"Store:update schema validation failed: ...\"",
                    "desc": "If the modified data fails schema validation."
                }
            ],
            "source": {
                "line": 1039,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "updateImmutable",
            "desc": "Applies updates to the session's data via a transform function.\nEnsures the transform runs without yielding and validates the result against the schema.\nRequires the use of copy-on-write semantics in transformFunction, where\nthe data is not directly mutated but instead a new copy is returned.",
            "params": [
                {
                    "name": "transformFunction",
                    "desc": "Function to modify data. Must return a new copy of the data with changes to commit changes, or `false` to abort.",
                    "lua_type": "(data: T) -> T | false"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with `true` if changes were committed, `false` if aborted by the transform.",
                    "lua_type": "Promise<boolean>"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Session is closed\"",
                    "desc": ""
                },
                {
                    "lua_type": "\"transformFunction failed: ...\"",
                    "desc": "If the transform function errors."
                },
                {
                    "lua_type": "\"transformFunction must return a boolean\"",
                    "desc": ""
                },
                {
                    "lua_type": "\"Store:update schema validation failed: ...\"",
                    "desc": "If the modified data fails schema validation."
                }
            ],
            "source": {
                "line": 1068,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "save",
            "desc": "Queues a save operation for the current session state if changes are pending.\nUses the session's PromiseQueue to serialize saves.",
            "params": [],
            "returns": [
                {
                    "desc": "Resolves when the save operation completes (or immediately if no changes).",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Session is closed\"",
                    "desc": ""
                }
            ],
            "source": {
                "line": 1091,
                "path": "src/Session.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "SessionImpl<T>",
            "desc": "Internal type definition for Session methods (used for metatable).\n\n\n-- Static method to load data and create a session\n\n-- Internal methods for saving data\n\n-- State checking\n\n-- Internal state mutation\n\n-- Autosave management\n\n-- Public API methods (called via Store)",
            "fields": [
                {
                    "name": "__index",
                    "lua_type": "SessionImpl<T>",
                    "desc": "Metatable self-reference."
                },
                {
                    "name": "load",
                    "lua_type": "(params: LoadSessionParams<T>) -> Promise<Session<T>>",
                    "desc": ""
                },
                {
                    "name": "updateRecord",
                    "lua_type": "(self: Session<T>) -> Promise<any>",
                    "desc": ""
                },
                {
                    "name": "orphanFile",
                    "lua_type": "(self: Session<T>, file: Types.File) -> ()",
                    "desc": ""
                },
                {
                    "name": "writeRecord",
                    "lua_type": "(self: Session<T>, txInfo: Types.TxInfo) -> Promise<any>",
                    "desc": ""
                },
                {
                    "name": "isSaved",
                    "lua_type": "(self: Session<T>) -> boolean",
                    "desc": ""
                },
                {
                    "name": "setData",
                    "lua_type": "(self: Session<T>, data: T) -> ()",
                    "desc": ""
                },
                {
                    "name": "mutateKey",
                    "lua_type": "(self: Session<T>, newData: T) -> ()",
                    "desc": ""
                },
                {
                    "name": "startAutosaving",
                    "lua_type": "(self: Session<T>) -> ()",
                    "desc": ""
                },
                {
                    "name": "stopAutosaving",
                    "lua_type": "(self: Session<T>) -> ()",
                    "desc": ""
                },
                {
                    "name": "unload",
                    "lua_type": "(self: Session<T>) -> Promise",
                    "desc": ""
                },
                {
                    "name": "get",
                    "lua_type": "(self: Session<T>) -> Promise<T>",
                    "desc": ""
                },
                {
                    "name": "update",
                    "lua_type": "(self: Session<T>, transformFunction: (data: T) -> boolean) -> Promise<boolean>",
                    "desc": ""
                },
                {
                    "name": "save",
                    "lua_type": "(self: Session<T>) -> Promise",
                    "desc": ""
                }
            ],
            "private": true,
            "source": {
                "line": 77,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "SessionProps<T>",
            "desc": "Internal state properties of a Session instance.",
            "fields": [
                {
                    "name": "_cleanupAutosave",
                    "lua_type": "(() -> ())?",
                    "desc": "Function to stop the running autosave loop, if active."
                },
                {
                    "name": "key",
                    "lua_type": "string",
                    "desc": "The unique key this session manages."
                },
                {
                    "name": "ctx",
                    "lua_type": "Types.StoreContext<T>",
                    "desc": "Shared context from the parent Store."
                },
                {
                    "name": "lockHandle",
                    "lua_type": "Locks.LockHandle",
                    "desc": "The active distributed lock handle for this key."
                },
                {
                    "name": "userIds",
                    "lua_type": "{ number }?",
                    "desc": "User IDs associated with this session (primarily for SetAsync)."
                },
                {
                    "name": "data",
                    "lua_type": "T?",
                    "desc": "The current, mutable in-memory data."
                },
                {
                    "name": "appliedMigrations",
                    "lua_type": "{ string }",
                    "desc": "List of migration step names already applied to this data."
                },
                {
                    "name": "changeSet",
                    "lua_type": "{ [string]: true }",
                    "desc": "Tracks unsaved changes. Keys are unique mutation IDs, value is always true. Cleared on successful save."
                },
                {
                    "name": "orphanedFiles",
                    "lua_type": "{ Types.File }",
                    "desc": "List of file shards (from previous saves) marked for deletion."
                },
                {
                    "name": "currentFile",
                    "lua_type": "Types.File?",
                    "desc": "Reference to the current file structure (potentially sharded) representing the saved state."
                },
                {
                    "name": "queue",
                    "lua_type": "PromiseQueue.PromiseQueue",
                    "desc": "PromiseQueue to serialize save/unload/tx operations for this key."
                },
                {
                    "name": "txLockPromise",
                    "lua_type": "Promise?",
                    "desc": "A promise used during multi-key transactions (`Store:tx`) to block concurrent `Session:update` calls on this key until the transaction completes."
                },
                {
                    "name": "closed",
                    "lua_type": "boolean",
                    "desc": "Flag indicating if the session is closed (e.g., lock lost, unloaded)."
                },
                {
                    "name": "unloadPromise",
                    "lua_type": "Promise?",
                    "desc": "Promise tracking an ongoing unload operation."
                },
                {
                    "name": "keyInfo",
                    "lua_type": "DataStoreKeyInfo?",
                    "desc": "DataStoreKeyInfo obtained during the initial load."
                },
                {
                    "name": "logger",
                    "lua_type": "Log.Logger",
                    "desc": "Logger instance specific to this session (includes key)."
                }
            ],
            "private": true,
            "source": {
                "line": 126,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "Session<T>",
            "desc": "Represents an active, locked session for a specific data key.",
            "lua_type": "typeof(setmetatable({} :: SessionProps<T>, {} :: SessionImpl<T>))",
            "source": {
                "line": 151,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "LoadSessionParams<T>",
            "desc": "Parameters for the static Session.load method.",
            "fields": [
                {
                    "name": "key",
                    "lua_type": "string",
                    "desc": ""
                },
                {
                    "name": "storeContext",
                    "lua_type": "Types.StoreContext<T>",
                    "desc": ""
                },
                {
                    "name": "userIds",
                    "lua_type": "{ number }?",
                    "desc": ""
                }
            ],
            "private": true,
            "source": {
                "line": 163,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "LoadParams",
            "desc": "Parameters for the internal load helper function.",
            "fields": [
                {
                    "name": "storeContext",
                    "lua_type": "Types.StoreContext<any>",
                    "desc": ""
                },
                {
                    "name": "key",
                    "lua_type": "string",
                    "desc": ""
                }
            ],
            "private": true,
            "source": {
                "line": 184,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "LoadResult",
            "desc": "Result structure for the internal load helper function.",
            "fields": [
                {
                    "name": "data",
                    "lua_type": "any",
                    "desc": ""
                },
                {
                    "name": "appliedMigrations",
                    "lua_type": "{ string }",
                    "desc": ""
                },
                {
                    "name": "orphanedFiles",
                    "lua_type": "{ Types.File }",
                    "desc": ""
                },
                {
                    "name": "currentFile",
                    "lua_type": "Types.File?",
                    "desc": ""
                },
                {
                    "name": "keyInfo",
                    "lua_type": "DataStoreKeyInfo?",
                    "desc": ""
                }
            ],
            "private": true,
            "source": {
                "line": 201,
                "path": "src/Session.luau"
            }
        },
        {
            "name": "CreateSessionParams",
            "desc": "Parameters for the internal createSession function.",
            "fields": [
                {
                    "name": "storeContext",
                    "lua_type": "Types.StoreContext<any>",
                    "desc": ""
                },
                {
                    "name": "key",
                    "lua_type": "string",
                    "desc": ""
                },
                {
                    "name": "lockHandle",
                    "lua_type": "Locks.LockHandle",
                    "desc": ""
                },
                {
                    "name": "userIds",
                    "lua_type": "{ number }?",
                    "desc": ""
                },
                {
                    "name": "appliedMigrations",
                    "lua_type": "{ string }",
                    "desc": ""
                },
                {
                    "name": "orphanedFiles",
                    "lua_type": "{ Types.File }",
                    "desc": ""
                },
                {
                    "name": "currentFile",
                    "lua_type": "Types.File?",
                    "desc": ""
                },
                {
                    "name": "keyInfo",
                    "lua_type": "DataStoreKeyInfo?",
                    "desc": ""
                }
            ],
            "private": true,
            "source": {
                "line": 393,
                "path": "src/Session.luau"
            }
        }
    ],
    "name": "Session",
    "desc": "Manages the in-memory state and persistence lifecycle for a single data key\nwithin a Store. A Session represents an active, locked instance of a key's\ndata, coordinating data loading, saving, updates, transactions, migrations,\nand cleanup.\n\nSessions are created internally by [Store:load] after acquiring a distributed\nlock via the Locks module. They handle:\n- Loading initial data by reading the main record, file shards, and pending\n  transactions, applying migrations, or importing legacy data.\n- Providing access to the current data ([Session:get]).\n- Applying mutations ([Session:update]) safely within a `noYield` context and\n  tracking changes.\n- Saving changes back to DataStores ([Session:save], [Session:updateRecord], [Session:writeRecord]),\n  potentially sharding large data via the Files module.\n- Coordinating multi-key transactions ([Store:tx] interacts with Session state).\n- Automatically saving data periodically ([Session:startAutosaving]).\n- Cleaning up orphaned file shards in the background ([Session:orphanFile]).\n- Gracefully unloading data ([Session:unload]), ensuring final saves and lock release.\n\nA Session is intrinsically tied to a [Locks.LockHandle]. If the lock is lost,\nthe Session becomes 'closed' and unusable, triggering cleanup.",
    "private": true,
    "source": {
        "line": 27,
        "path": "src/Session.luau"
    }
}