Lock step

Stop two Glue job runs from doing the same work at once.

The Lock step makes sure two runs don't do the same work at the same time. A run takes a lock on a key, and any other run that asks for the same key while it's held waits, skips or fails, depending on how you set it up.

When to use it

  • The same webhook arrives twice at once, and both copies would create the same customer or order.
  • A running total in the Cache would lose an update if two sales read and write it at the same moment.
  • A scheduled job sometimes runs longer than its interval, and the next run shouldn't start until it's done.

Set it up

In the appdata fieldWhat it does
KeykeyWhat to lock. The same key is shared by every run, so customer:{{ email }} locks one customer and nightly-export locks the whole job. Liquid works.
ScopescopeJob (the default) or Block. See below.
Time to live (seconds)ttl_secondsA safety expiry in case a run crashes. Default 300, up to 3600.
Wait for lock (seconds)wait_secondsHow long to wait for a busy lock before giving up. Default 0, up to 60.
When busyon_busyError (the default), Skip or Continue. See below.
Response variableresponse_variableSaves true when this run holds the lock, and false when it was busy.

Scope

  • Job holds the lock until the run ends, whether it succeeds, fails with an error, is stopped by a Stop Job step, or is cleaned up after a crash.
  • Block holds the lock only while the steps nested under it run, and releases it straight after, even if one of them fails. Drag the steps you want protected into the Lock's LOCK branch.

If the same run asks for a lock it already holds, it gets it again straight away (the response variable is true).

When busy

When busyScope JobScope Block
ErrorThe step fails and the job is retried later.The step fails and the job is retried later.
SkipThe rest of the run ends quietly, as a success.The nested steps are skipped, and the job carries on after the Lock.
ContinueThe job carries on without the lock.The same as Skip: the nested steps are skipped, then the job carries on.

With Error, the step fails with a message like lock "customer:[email protected]" is busy (waited 10s); the job will be retried.

🚧

Set the time to live above the job's longest run

Time to live is only a safety net for a run that crashes. It isn't refreshed while the job runs, so if a run takes longer than its time to live, the lock expires and another run can take it. Set it comfortably above the worst-case runtime of the steps it protects.

Example 1: Create a customer once, even when the webhook arrives twice

Trigger: Webhook.

  1. Script: Read the payload, which sets $.email.
  2. Lock: Lock the customer, Key customer:{{ email }}, Scope Job, Wait for lock 10, When busy Error.
  3. Cache: Read the cached customer, Operation Get, Key customer:{{ email }}, Response variable cached_id.
  4. If Condition: If the customer is new, Condition $.cached_id === null. Under THEN:
    1. API Request: Create the customer, Response variable customer_res.
    2. Cache: Store the customer, Operation Set, Key customer:{{ email }}, Value set from the new customer's ID.

The second copy of the webhook waits up to 10 seconds. By the time it gets the lock, the first copy has cached the customer, so the second one finds it and creates nothing. If the first copy is still running after 10 seconds, the second fails and is retried later.

Example 2: Keep a running total safe

Trigger: System Hook, Create Sale.

  1. Script: Read the sale, which sets $.sale and $.sale_total.
  2. Lock: Lock this sale, Key daily-total, Scope Block, Wait for lock 30, When busy Error. In its LOCK branch:
    1. Cache: Read the running total, Operation Get, Key sales:total, Default value 0, Response variable running.
    2. Script: Add this sale: $.new_total = Number($.running) + Number($.sale_total);
    3. Cache: Store the total, Operation Set, Key sales:total, Value {{ new_total }}, Value type Number.
  3. API Request: Post the sale, at the top level, after the lock is released.

Only the read-add-write is locked, so other sales wait a moment rather than for the whole job. (For a simple counter, the Cache step's Increment is already safe without a lock.)

Tips

  • Lock as little as you can. Use Block around just the steps that need it, so other runs aren't held up.
  • Use Skip for "only one at a time" schedules. A scheduled job with a Job lock and Skip quietly lets a run go if the previous one is still working.
  • Check the response variable if you use Continue, to know whether you really have the lock.

← All Glue step guides


Did this page help you?