Race Conditions


The components triggering events in your app have one goal. They want to complete their actions as quickly as possible. In a perfect world, they all have their turn. But what happens when they run into other events trying to complete the same action at the same time?

We call this a race condition, and it can affect how your end-users interact with your application. A race condition occurs with 2 or more components or data submissions. The race condition triggers when they're competing to be the first to complete an action.

In your applications, race conditions can disorganize information and create confusing errors. The best way to avoid them is to prevent them from happening in the first place. How you do that depends on what type of race conditions your app is susceptible to.

What You'll Learn

In this article, you'll learn about:

What are Data-Related Race Conditions?

When end-users submit data at the same time, a data-related race condition can happen in 2 ways. One is many people trying to submit data. The other is a single person using many devices trying to submit data.

Suppose your team got a request to update a paragraph on your website. Without knowing, you and a coworker both decide to make the change. The race condition triggers when both of you try to publish your change at the same time.

The software publishes the changes it received first, down to the millisecond. This is a problem for the runner-up if the software doesn't communicate what happened. They'll wonder why the software confirmed their update but didn't publish their version.

The best method of prevention here is locking your data and adding error language.

How to Lock Data

There are 2 ways to lock data and prevent race conditions. To see live examples of both, check out our Locking Training Lab.

Optimistic Locking

An optimistic lock allows changes to data if someone else is on a page. It only intervenes when issues arise.

Think of the glass half full. An optimistic lock assumes nothing bad will happen if 2 or more people work in the same place.

After submission, an optimistic lock pushes an error. It tells the runner-up that there's new information and instructs them to refresh the page.

You might use an optimistic lock for:

  • A banking app where 2 people in a joint account want to transfer funds at the same time.

  • A group food order with many people changing their orders in real-time.

Pessimistic Locking

A pessimistic lock prevents the end-user from changing data if someone else is on the page. It assumes nothing good can happen if more than 1 person works in the same place.

Pessimistic locks stop changes before they happen. This issue tells the runner-up that the data on this page is currently locked by someone else. Until this person leaves, the runner-up can't make changes.

You might use a pessimistic lock for:

  • Roommates sending their applications for the same lease.

  • An office supply order with many people adding and removing items.

Configuration-Related Race Conditions

Components need 2 things to give you your desired output. They must trigger in the right order and communicate with dependent components.

Sometimes a component relying on the API (application programming interface) call triggers while that API is still processing. We call this a configuration-related race condition. The component will have fired before it had all the information to perform its action.

Think of it as a relay race. Components are the runners who have to wait to get their data, or baton, before kicking off. Synchronous components will wait to receive the baton before they start running. But asynchronous components start running before they get their baton.

This can lead to problems, depending on your setup. Issues can range from incorrect page information to interfering with other processes. Unlike data-related race conditions, refreshing the page won't fix the situation.

Best Practices for Synchronous Components

Components that trigger synchronously are some of the most common components. They execute one-by-one, waiting until the previous components are complete before moving forward. Initializer, Calculator, and Decisions components are all synchronous.

There are 2 best practices to prevent these race conditions. Be sure to use the appropriate synchronous component. And sequence your events in the order that they should occur. This prevents the flow from moving forward until all actions are complete.

Best Practices for Asynchronous Components

Components that trigger asynchronously will execute many components at the same time. Even if previous components are still running, other components continue triggering. Plug-Ins, Timers, and other components with post-trigger settings run asynchronously.

Asynchronous components are great when you need to execute many actions quickly. The key to avoiding race conditions is to keep dependencies small.