SafeSale · Blog

How long a Shopify sale takes to apply (and what X / Y is)

Why a scheduled Shopify sale can take a minute to reach the storefront, what the applied count means while it runs, and what a retry or dead job looks like.

It is 9:00. The sale you scheduled last night is due. You refresh the storefront and the prices are the old ones. You refresh again. Still old. At 9:01 half of the collection is on sale and the other half is not. By 9:03 it is all done and nobody but you noticed. That minute is the subject of this post, because the first time you watch it you will wonder whether to do something, and the honest answer is almost always no.

Below is what SafeSale does between the start time and the last variant, where the time goes, what the progress counter on the sale page is counting, and how to tell a slow sale from a stuck one.

Step zero: the scheduler tick

A scheduled sale is a job with a run time. SafeSale looks for due jobs every 30 seconds, so a sale due at 9:00:00 may be picked up anywhere between 9:00:00 and 9:00:30. That is the first and smallest part of the delay, and it is the same for every kind of sale. If your start time is a hard deadline, such as a countdown on the homepage hitting zero, schedule the sale for one minute earlier than the countdown and nobody will see a gap.

Automatic discount mode: one write and done

If the sale runs as an automatic discount, the job creates a single discount in Shopify and saves its id. Shopify applies the discount in the cart and at checkout from that moment on. There is nothing to loop over, so a sale on 20 variants and a sale on 20,000 variants take the same time: one scheduler tick plus one API call. The sale page moves from Scheduled to Active without passing through a progress counter.

The thing you trade for that speed is the product page. An automatic discount does not change the variant price, so the storefront shows the regular price until the item is in the cart unless the theme reads the discount itself. The comparison of the two mechanisms goes into when that matters.

Price rewrite mode: snapshot, then batches

A price rewrite genuinely edits every variant, so it has to do the work variant by variant. The job runs in a fixed order and every step is safe to repeat.

  1. Snapshot. Before any price moves, the current price and compare-at of every targeted variant are stored. This is one database transaction and it is insert only: a retry never overwrites a snapshot with an already discounted price. The sale's history gets a line reading "Snapshotted original prices for N variants".
  2. Plan from the snapshot. The new prices are computed from the stored originals, not from whatever Shopify currently shows. Variants that are blocked by your hard cap, that would not change, or that were already written on a previous attempt are left out of the pending list.
  3. Write per product, 250 variants at a time. Shopify updates variant prices through a mutation that takes one product and a list of its variants, so the pending list is grouped by product. Products with more variants than that are split into several calls. After each call the snapshot rows are marked applied, a change log entry is written for each variant, and the sale's applied count goes up by the size of the batch.
  4. Mark active. When the pending list is empty the sale flips from Applying to Active.

So the time a price rewrite takes is roughly one API round trip per product (more for products with hundreds of variants), plus the database writes around each one. A sale across 300 products with a few variants each is 300 round trips. That is the minute or two you watched at 9:00. Shopify's API rate limits can stretch it further during busy periods, which is exactly when you are most likely to be running a sale.

Reading X / Y

While the sale is Applying, the sale page shows two numbers, written as applied over total. Total is the number of variants the plan intends to change. Applied is how many have been written and confirmed. Because the count increases by one batch at a time, it jumps in steps: a product with 12 variants adds 12 at once. A counter that has not moved for ten seconds is usually in the middle of a call, not stuck.

Two details are worth knowing. Variants whose current price already equals the planned sale price are skipped and never counted in applied, so the final number can be a little below total on a sale that overlaps an earlier one. And if you cancel during Applying, the revert uses the same snapshot and the same batching in reverse, with its own "Restored N variants (M already matched)" line in the history.

Slow, retrying, or dead

Three states look similar from the storefront and are very different on the sale page.

  • Slow. Status is Applying, the counter moves, there is no error. Leave it alone. Opening the sale and pressing buttons does not speed it up.
  • Retrying. A Shopify call failed, often a throttle or a brief outage. The job keeps everything it already wrote and schedules another attempt: 30 seconds after the first failure, then one minute, two, four, and so on, capped at one hour between attempts. The sale page shows the last error text. Up to eight attempts are made before the job gives up.
  • Dead. All attempts failed. The job is never silently dropped; it is marked dead and surfaced on the sale page with a Retry button, and the app's queue health check reports it. Fix the underlying cause (the last error tells you whether it was a permission, a deleted product or Shopify itself) and press Retry. Because every step is idempotent, the retry resumes from the last written batch rather than starting over.

An ending sale behaves the same way, with one extra rule: if the revert job dies, the sale is flagged Failed so you cannot miss that prices are still discounted. The guide to the Cancel, End and Retry buttons covers that screen.

Making the start feel instant

  • Schedule the start a minute or two before the announced time. Shoppers see the sale when they arrive, not a half-applied collection.
  • Prefer automatic discount mode for very large catalogs when product page pricing is not essential, since it applies in a single step.
  • Keep the number of products in a single sale sensible. Two sales of 150 products each start no faster than one of 300, but they fail and retry independently.
  • Watch the applied count on the sale page rather than refreshing the storefront. The page updates as batches land and tells you about errors; the storefront only tells you that something has not happened yet.

A sale that takes a minute to apply is not a sale with a problem. It is a few hundred price changes being written carefully, with a snapshot taken first and a retry path for every step. SafeSale shows you exactly how far it has got so the waiting is at least informed.

Frequently asked questions

My sale was scheduled for 9:00 and the storefront still showed old prices at 9:00. Is something wrong?

Probably not. SafeSale checks for due work every 30 seconds, and a price-rewrite sale then writes prices product by product. A few hundred variants are usually done within a minute or two of the start time. If the sale page says Applying and the count is moving, it is working. If it says Failed or shows a dead job, open the post for what to do.

What does the X / Y on the sale page mean?

Y is the number of variants the sale plans to change. X is how many have been written to Shopify so far. The count moves up one batch at a time, where a batch is up to 250 variants of one product, so it jumps rather than ticking by one.

Is an automatic discount faster than a price rewrite?

Yes. In automatic discount mode SafeSale creates one discount and Shopify applies it at checkout immediately, so the sale is live within one scheduler tick regardless of how many variants it covers. The trade-off is that product pages do not show a struck-through price unless your theme handles it.

What happens if Shopify is slow or returns an error half way through?

The job retries with a growing delay: 30 seconds, then one minute, two, four, up to an hour between attempts, for a maximum of eight attempts. Every step is written so a retry picks up where the last one stopped and never discounts an already discounted price. After the last attempt the job is marked dead and shown on the sale page with a Retry button.