Skip to content

Profile ​

Session-locked profile, returned by store:load. Implements ProfileBase plus lock management.

Fields ​

FieldTypeDescription
kind"locked"
keystringDatastore key.
openbooleanfalse once closed; most methods fail with profile_closed then.
is_lockedbooleanWhether the session lock is held. true after load, false after release.
lock_idstringId of this acquisition's lock ({job_id}#{n}), re-stamped unchanged on every write and replaced by readquire. See Session lock.
last_writenumberTime of the last successful write.
user_ids{ number }As passed to load.
migrations{ string }Migration names applied to this record.
configResolvedConfig<T>The store's resolved config.
datastoreDatastoreHandleThe store's handle.

Methods ​

profile:get_data() ​

luau
(self: Profile<T>) -> T

Current data, deep-frozen. Never mutate it. Throws a profile_closed error table if the profile is closed (a plain accessor, no Result).

profile:update(fn) ​

luau
(self: Profile<T>, fn: DataDispatcher<T>) -> Result<boolean>
-- DataDispatcher<T> = (T) -> (T | false)?

Calls fn with the current data. Returning a new value replaces the data (fires on_change, marks dirty); returning false or nil leaves it untouched. Ok(true) when the data changed, Ok(false) otherwise. Fails with profile_closed or released (released). An error thrown by fn propagates as is.

profile:save() ​

luau
(self: Profile<T>) -> Result<nil>

Writes the data now (fires on_save first) and refreshes the lock. Yields. Fails with profile_closed, released, roblox (storage kept failing after retries), predicate_failed (config.predicate rejected the stored data: nothing was written, the lock is kept) or lock_lost (another server took the key: the profile is closed, nothing was written).

profile:release() ​

luau
(self: Profile<T>) -> Result<nil>

Writes the data and gives the session lock back, keeping the profile open so another server may take the key. Afterwards is_locked is false; update / save fail with released, no autosave runs, the profile cannot join a transaction and unload writes nothing. No-op when already released. Fails with profile_closed, roblox or predicate_failed (the lock is still ours, is_locked stays true), or lock_lost (the profile is closed).

profile:readquire() ​

luau
(self: Profile<T>) -> Result<nil>

Takes the session lock again, waiting like load does, and adopts the stored record (resolved pending, migrations) as the current data, firing on_change. Fails with timeout (leaving the profile open and released), profile_closed, roblox, outdated (a newer server wrote the record; no lock is taken, the profile stays released), predicate_failed (the stored data did not pass config.predicate; no lock is taken either), migration_mismatch or tx_marker_invalid. No-op when already locked.

profile:unload() ​

luau
(self: Profile<T>) -> ()

Fires on_closing, closes the profile, writes the data and releases the lock (when locked), then fires on_closed. No-op when already closed. A failed final write is reported with warn.

profile:wait_settled() ​

luau
(self: Profile<T>) -> ()

Yields until no operation holds or waits on the profile mutex. After it returns, the next update is guaranteed not to yield.

profile:wait_closed() ​

luau
(self: Profile<T>) -> ()

Yields until the profile is closed. Throws when called on an open profile (misuse).

Events ​

luau
profile:on_change(function(new: T, old: T?) end)
profile:on_save(function() end)
profile:on_closing(function() end)
profile:on_closed(function() end)
profile:on_lock_lost(function() end)
EventFires when
changeThe data was replaced: update, readquire, a committed transaction. (The store-level on_change also reports the data a key loads with; a profile's own hooks cannot, they are attached after the load.)
saveJust before a write (save, autosave, release, unload).
closingAt the start of unload, while the profile is still open. Last chance to update before the final write.
closedOnce the profile is closed and its final write, if any, is done. Also after lock_lost.
lock_lostA write found the lock owned by someone else, or it expired. The profile is closed and nothing was written.

ProfileBase ​

The part shared by locked and lockless profiles. Transactions accept any ProfileBase.

luau
type ProfileBase<T> = {
	kind: "locked" | "lockless",
	key: string,
	config: ResolvedConfig<T>,
	user_ids: { number },
	open: boolean,
	migrations: { string },

	get_data: (self) -> T,
	update: (self, (T) -> T | false) -> Result<boolean>,
	save: (self) -> Result<nil>,
	unload: (self) -> (),
	wait_settled: (self) -> (),

	on_change: (self, callback: (new: T, old: T?) -> ()) -> (),
	on_save: (self, callback: () -> ()) -> (),
	on_closing: (self, callback: () -> ()) -> (),
	on_closed: (self, callback: () -> ()) -> (),
	on_lock_lost: (self, callback: () -> ()) -> (),
}