Component locking

The component locking feature enables members to optimistically lock stateful UI components before editing them. This reduces the chances of conflicting changes being made to the same component by different members. A component could be a cell in a spreadsheet that a member is updating, or an input field on a form they're filling in.

Once a lock has been acquired by a member, the component that it relates to can be updated in the UI to visually indicate to other members that it is locked and which member has the lock. The component can then be updated once the editing member has released the lock to indicate that it is now unlocked.

Each lock is identified by a unique string ID, and only a single member may hold a lock with a given string at any one time. A lock will exist in one of three states and may only transition between states in specific circumstances.

Lock states

Component locking is handled entirely client-side. Members may begin to optimistically edit a component as soon as they call acquire() on the lock identifier related to it. Alternatively, you could wait until they receive a locked event and display a spinning symbol in the UI until this is received. In either case, a subsequent unlocked event may invalidate that member's lock request if another member acquired it earlier. The time for confirmation of whether a lock request was successful or rejected is, on average, in the hundreds of milliseconds. However, your code should handle all possible lock state transitions.

A lock will be in one of the following states:

StateDescription
pendingA member has requested a lock by calling acquire().
lockedThe lock is confirmed to be held by the requesting member.
unlockedThe lock is confirmed to not be locked by the requesting member, or has been released by a member previously holding the lock.

The following lock state transitions may occur:

  • None → pending: a member calls acquire() to request a lock.
  • pendinglocked: the requesting member holds the lock.
  • pendingunlocked: the requesting member does not hold the lock, since another member already holds it.
  • lockedunlocked: the lock was either explicitly released by the member, or their request was invalidated by a concurrent request which took precedence.
  • unlockedlocked: the requesting member reacquired a lock they previously held.

Only transitions that result in a locked or unlocked status will emit a lock event that members can subscribe() to.

Acquire a lock

Use the acquire() method to attempt to acquire a lock with a given unique ID. Additional attributes may be passed when trying to acquire a lock that can contain a set of arbitrary key-value pairs. An example of using attributes is to store the component ID the lock relates to so that it can be easily updated in the UI with a visual indication of its lock status.

A member must have been entered into the space to acquire a lock.

The following is an example of attempting to acquire a lock:

JavaScript

The following is an example of passing a set of attributes when trying to acquire a lock:

JavaScript

The following is an example payload returned by space.locks.acquire(). The promise will resolve to a lock request with the pending status:

JSON

Once a member requests a lock by calling acquire(), the lock is temporarily in the pending state. An event will be emitted based on whether the lock request was successful (a status of locked) or invalidated (a status of unlocked). This can be subscribed to in order for the client to know whether their lock request was successful or not.

Release a lock

Use the release() method to explicitly release a lock once a member has finished editing the related component. For example, the release() method can be called once a user clicks outside of the component, such as clicking on another cell within a spreadsheet. Any UI indications that the previous cell was locked can then be cleared.

The following is an example of releasing a lock:

JavaScript

Releasing a lock will emit a lock event with a lock status of unlocked.

Subscribe to lock events

Subscribe to lock events by registering a listener. Lock events are emitted whenever the lock state transitions into locked or unlocked. Use the subscribe() method on the locks namespace of the space to receive updates.

All lock events are update events. When a lock event is received, UI components can be updated to add and remove visual indications of which member is locking them, as well as enabling and disabling the ability for other members to edit them.

The following is an example of subscribing to lock events:

JavaScript

The following is an example payload of a lock event:

JSON

The following are the properties of a lock event payload:

PropertyDescriptionType
idThe unique ID of the lock request.String
statusThe lock status of the event. Will be one of locked, unlocked, or pending.String
timestampThe timestamp of the lock event.Number
attributesThe optional attributes of the lock, such as the ID of the component it relates to.Object
reasonIf set, gives the reason for status. Successful lock requests do not set a reason.ErrorInfo
member.clientIdThe client identifier for the member.String
member.connectionIdThe unique identifier of the member's connection.String
member.isConnectedWhether the member is connected to Ably or not.Boolean
member.lastEvent.nameThe most recent event emitted by the member. Will be one of enter, update, present, or leave.String
member.lastEvent.timestampThe timestamp of the most recently emitted event.Number
member.profileDataThe optional profile data associated with the member.Object

The following are the error codes that are returned in the reason property:

Error codeDescription
101002There is an existing lock request in the pending or locked state. Nested locks are not supported, so the previous request must be handled before a new lock is requested.
101003The lock is currently in the locked state, and the pending request did not invalidate the status.
101004The lock request invalidated an existing lock in the locked state.

Unsubscribe from lock events

Unsubscribe from lock events to remove previously registered listeners.

The following is an example of removing a listener for lock update events:

JavaScript

Or remove all listeners:

JavaScript

Query lock status

Use the get() method to query whether a lock is currently locked, and by which member if it is. The lock is identifiable by its unique string ID.

The following is an example of checking whether a lock identifier is currently locked:

JavaScript

The following is an example of checking which member holds the lock:

JavaScript

The following is an example of viewing the attributes assigned to the lock by the member holding it:

JavaScript

If the lock is not currently held by a member, get() will return undefined. Otherwise, it will return the most recent lock event for the lock.

Retrieve locks

Locks can also be retrieved in one-off calls. These are local calls and retrieve the locks retained in memory by the SDK.

The following is an example of retrieving a member's own currently held locks:

JavaScript

The following is an example payload returned by space.locks.getSelf()

JSON

The following is an example of retrieving the locks held by all members other than the member themselves:

JavaScript

The following is an example payload returned by space.locks.getOthers()

JSON

The following is an example of retrieving an array of all currently held locks in a space:

JavaScript

The following is an example payload returned by space.locks.getAll()

JSON

Example usage

The following is an example of the steps involved in implementing component locking.

JavaScript
Select...