Skip to Content

Event concurrency and locking

Concurrency and locking

By default, events execute concurrently. If two users click the same button at the same time, the event executions may overlap. In some situations, it may be necessary to synchronize events. Synchronized events execute in a serial fashion, ensuring that only one synchronized event executes at a time. Non-synchronized events may still execute concurrently, even with synchronized events.

Events are synchronized with locks. A lock encompasses the event's validation and actions. Locks do not synchronize other rules, such as defaults or visibility rules.


In a multi-server environment, distributed locks ensure that events are synchronized across the cluster. When using Redis as the shared state provider, the Redlock algorithm is used.

To enable event locking, check the Use Lock option when creating or modifying the event. This will enable additional lock options. Use Lock is available from the Event configuration screen, by clicking Edge Case.


Use care when choosing whether to lock an event. Synchronizing events negatively impacts performance.

Lock names

Event locks are named locks. The name determines the scope of the lock. Locked events sharing the same name will be synchronized. This allows developers to synchronize multiple events. For instance, developers can synchronize the Insert, Update and Delete events for a given table. Events from different tables can even be synchronized with one another if they share the same name.

If the developer does not specify a lock name, a default name will be generated at runtime. The name generated depends on whether the event is a table-level event or a row-level event. Table-level events include the intrinsic Filter and New events. Row-level events include the intrinsic Insert, Update, and Delete events, as well as user-defined events.

For table-level events, the default name scope's the lock to the table. Locking the table's Filter event, for instance, ensures that only one Filter event can execute at a time for the given table.

For row-level events, the default name scope's the lock to a row. Locking the table's Update event, for instance, ensures that only one Update event can execute at a time for a given row. Two Update events could execute concurrently for different rows in the same table.

Because lock names are global, developers must take care to properly namespace lock names. Consider using the following pattern as a baseline when naming locks:


For example:



If two events share the same lock name, Vinyl will issue a warning to avoid accidental collisions.

Lock names support string interpolation. String interpolation substitutes values from the current row into the lock name. Typically, the row's primary key values are substituted into the name to create row-scoped locks.

The string interpolation syntax is:

{{ column-name }}

For example, given the following lock name:

Fulfillment:Orders:Ship:{{ OrderId }}

The runtime lock name might look something like this:


Lock expiry

Typically, locks are held until the event completes, successfully or otherwise. If the event does not complete in a timely fashion, it will affect the availability of the system. To mitigate this, event locks expire. If the event does not complete within the expiry period, the lock is released. Note, however, that the event may continue to execute.

The default expiry is 10 seconds. Developers can change the expiry as necessary. Caution should be used with higher expiry values.

Lock wait

Locks are exclusive: only one event can acquire and hold a given lock at a time. Other events attempting to acquire the lock are queued. They will remain queued until they acquire the lock in turn or until the lock wait period has lapsed.

The default lock wait is 10 seconds. Developers can change the wait as necessary. If the expiry is relatively low, consider using a value equal to the expiry. If the expiry is high, consider using a low value for the lock wait period. In other words, if the lock is only expected to last a few seconds, users are typically willing to wait for the lock to become available. If, on the other hand, the lock is expected to last for a minute or more, then it is usually preferable to timeout the wait early and notify the user.

If the lock wait timeout is hit the user will see a message alert: "The event was canceled":

Lock inheritance

Events support inheritance. The validations and actions defined on a physical table are inherited by data object events of the same name. Events also inherit lock configuration. If the physical table's Update event is locked, then the data object's Update event is also locked.

Lock inheritance applies to the Save event as well. The Save event is not a first-class event. Instead, it's validations and actions are merged into the Insert and Update events at runtime. This allows developers to register a rule once and have it apply to both inserts and updates. Similarly, locking the Save event will lock the Insert and Update events.

Vinyl synthesizes a Change event at runtime using validations from the Insert or Update events. The Change event runs when a user modifies a field value. If Insert or Update event is locked, then the Change event will be locked as well.