A 60-second auto-suspend bills 30% more a cycle than a 300-second one

  • Saurav Raj Joshi
  • 15 min read

108 billed warehouse seconds a query cycle against 83, and the 108 belongs to the thrifty-looking setting. On the numbers here, a Snowflake warehouse on a 60-second auto-suspend costs more than the same warehouse on 300. Two mechanisms get blamed — the billing minimum charged on every resume, and the cache a warehouse drops when it suspends — and only the second is live at 60. The first is a myth, and Snowflake’s own documentation is where people get it.

The auto-suspend setting that minimises Snowflake credits is decided by the gap between queries, not by the 60-second billing minimum. Between a 60-second and a 105-second gap the longer setting wins: at an 80-second gap a 60-second auto-suspend bills 108 seconds a cycle against 83 for a 300-second one. Above 105, the short setting wins. Snowflake’s default is 600.

What Snowflake charges every time a warehouse resumes

One minute of warehouse time, charged again on every resume.

Snowflake puts the rule in an Important callout on its page about understanding compute cost: the meter runs per second, under a “60-second (i.e. 1-minute) minimum”. First bullet:

Each time a warehouse is started or resumed, the warehouse is billed for 1 minute’s worth of usage based on the hourly rate shown above.

The first section of the warehouse considerations page, “How are credits charged for warehouses?”, carries the sentence I’d put on a wall: “There is no benefit to stopping a warehouse before the first 60-second period is over because the credits have already been billed for that period.” Two-thirds down, the same page turns the rule into advice, and this article disagrees with it:

However, the value you set should match the gaps, if any, in your query workload. … each time it resumes, you are billed for the minimum credit usage (that is, 60 seconds).

Snowflake’s first clause there is the thesis of this article and I’d sign it. The reason attached to it is wrong. Anyone following the advice anyway has an honest motive: idle warehouse time is the most visible waste on a bill.

But run the interval and the minimum disappears. At a 60-second auto-suspend a running interval is the query plus the idle tail after it, so a query of q seconds bills q + 60. For any query at all, that is 60 or more. On a single-cluster warehouse the minimum never binds. Not once. It would bind only below 60, which Snowsight’s minutes field can’t express, so the mechanism everyone cites is dead at the setting everyone blames it for. Set AUTO_SUSPEND to 10 through SQL and the minimum is live again, which is a reason not to. Multi-cluster warehouses are the other exception, because the same page bills “The length of time the compute resources in each cluster runs” and a cluster can stop inside a minute.

Why is the first query slow after a Snowflake warehouse resumes?

Because the warehouse threw away its cache of table data when it suspended, and the next query pulls every micro-partition over the network again. The warehouse considerations page asks “How does warehouse caching impact queries?” and answers in one line: “This cache is dropped when the warehouse is suspended, which might result in slower initial performance for some queries after the warehouse is resumed.” The account usage views call it the local disk cache.

The cache scales with the warehouse: “the larger the warehouse and, therefore, more compute resources in the warehouse, the larger the cache”, says the same section. A bigger one has more cache to lose, and bills more per idle second.

Resume latency is not the cost. Snowflake puts provisioning at “generally very fast (e.g. 1 or 2 seconds)”, in the list of reasons it gives for disabling auto-suspend altogether. Nobody pays for a one-second wait. The bill is in the re-read: three seconds against a warm cache becomes tens once it is empty and every byte arrives over the network. The caching section itself ends on the choice: a “trade-off between saving credits by suspending a warehouse versus maintaining the cache”.

Snowflake also provisions a warehouse of its own inside every account, on what the warehouses overview calls “a 60-second default timeout”. Hold on to that.

Pick your auto-suspend setting from the gap between queries

Set the two per-cycle bills equal and the threshold falls out. A Snowflake warehouse that stays awake through the gaps bills the query plus the whole gap after it, w + g, for a warm query of w seconds and a gap of g measured from one query’s end to the next one’s start. On an auto-suspend A shorter than that gap it bills A seconds of idle tail, suspends for free, then a cold query of c seconds: A + c. The two cost the same when w + g = c + A. Cost is the only thing being set equal there, and a dashboard tile that takes 48 seconds instead of 3 is a separate argument.

Rearranged, g* = (c - w) + A.

Call it the cold-cache window: the range of gaps over which a longer auto-suspend bills less than a shorter one. Its lower edge is A, below which neither setting ever fires, and its width above A is exactly the cold-read penalty in seconds, so a 20-second penalty buys a 20-second window and a two-minute penalty a two-minute one.

You’ll need your own cold-read penalty, and Snowflake publishes the query that gets you close, under “Finding data scanned from cache” on its cache page. Reformatted here with trailing commas:

sql

SELECT warehouse_name,
       COUNT(*) AS query_count,
       SUM(bytes_scanned) AS bytes_scanned,
       SUM(bytes_scanned*percentage_scanned_from_cache) AS bytes_scanned_from_cache,
       SUM(bytes_scanned*percentage_scanned_from_cache) / SUM(bytes_scanned)
           AS percent_scanned_from_cache
FROM   snowflake.account_usage.query_history
WHERE  start_time >= dateadd(month,-1,current_timestamp())
AND    bytes_scanned > 0
GROUP  BY 1
ORDER  BY 5;

Two things about that query. The shared SNOWFLAKE database is visible to everybody but its ACCOUNT_USAGE schema is not: the Account Usage reference says access “can be granted by a user with the ACCOUNTADMIN role”, through GRANT IMPORTED PRIVILEGES or a SNOWFLAKE database role. And the column named percentage is not a percentage: the QUERY_HISTORY view says it “ranges from 0.0 to 1.0” and tells you to multiply by 100. The queries dragging that number down ran just after a resume.

A dashboard warehouse costed at auto-suspend 60 and at 300

Kettlebridge is invented, and on its numbers the 60-second setting bills 108 seconds a cycle against 83. It sells mooring and berth management software, employs thirty-one people, and has never been a client or an employer of ours. Its inputs are picked for legible arithmetic, none off a real account.

They run on a Large first-generation standard warehouse, 8 credits an hour on the warehouse overview table — a size up from the Medium they use in our piece on why a warehouse bill climbs on flat data, because the cache argument bites hardest where the cache is biggest. A warm query takes 3 seconds, the first after a resume 48, queries come 80 seconds apart end to start, a sitting runs 60 queries, and the day holds 9 sittings.

The window first. g* = (48 - 3) + 60 = 105 seconds, and an 80-second gap sits inside it, so the long setting should win. At 300 seconds the warehouse never suspends and bills 3 + 80 = 83 a cycle; at 60 it bills 60 + 48 = 108. And 108 ÷ 83 = 1.30, so the short setting bills 30% more.

Billed warehouse seconds per query cycle on Kettlebridge’s numbers, by auto-suspend setting and gap:

Gap between queries At 60 s At 300 s
40 s 43 43
80 s 108 83
105 s 108 108
160 s 108 163

A sitting is not 60 identical cycles. At 300 seconds the first query is cold, the other 59 warm, and a 300-second tail closes it: 48 + (59 × 83) + 300 = 5,245 seconds. At 60 seconds every query is cold: 60 × 108 = 6,480. Nine sittings gives 47,205 against 58,320 billed seconds, a difference of 11,115 a day, and 58,320 ÷ 47,205 = 1.235, so over the day the penalty is 23.5%, not 30%.

Now the ladder, because seconds aren’t credits. Bottom-up: 58,320 ÷ 3,600 seconds an hour = 16.2 billed hours at 60, 16.2 × 8 credits an hour = 129.6 credits, against 47,205 ÷ 3,600 = 13.11 hours and 104.9 credits at 300. Top-down: nine sittings of 60 × (48 + 80) = 7,680 seconds is 69,120 seconds of wall clock, 19.2 hours, a Large flat out that long is 19.2 × 8 = 153.6 credits, and it is awake 108 of every 128 seconds, so 153.6 × 0.84375 = 129.6. One model entered from opposite ends, so the agreement checks the wiring rather than the world.

The 300-second setting isn’t free either. Nine sittings of 300 seconds of trailing idle is 2,700 seconds a day, 0.75 hours, 6 credits that buy nothing.

The tail sets a second crossover too, and this one is in queries rather than seconds. At 300 a sitting of n queries bills 48 + 83(n - 1) + 300; at 60 it bills 108n. Set those equal: 108 - 83 = 25 a query on one side, 48 - 83 + 300 = 265 of fixed cost on the other, so 25n = 265 and n = 10.6. Kettlebridge’s sixty sit well past that. A sitting of ten does not: 108 × 10 = 1,080 against 48 + (9 × 83) + 300 = 1,095, and the thrifty setting wins after all, because the 300-second tail is paid once and ten queries are too few to spread it over. Below about eleven queries a sitting I wouldn’t touch the parameter.

AUTO_SUSPEND is in seconds, and 0 does not mean zero

Zero means never. The CREATE WAREHOUSE reference gives AUTO_SUSPEND its default in the “Optional properties (objectProperties)” block — “600 (the warehouse suspends automatically after 10 minutes of inactivity)” — and one line above that, in the valid values, sits the sentence that catches people out: “Setting a 0 or NULL value means the warehouse never suspends.”

The units differ between the two places you set it. Snowsight’s field is “Suspend After (min)”, and the SQL takes seconds: “Use the ALTER WAREHOUSE command to change the auto-suspend time limit, which is specified in seconds, not minutes”, says the cache page under “How to configure auto-suspension”.

The third quirk moves the arithmetic. That same valid-values list says the suspender is a poller: “The background process that suspends a warehouse runs approximately every 30 seconds … the setting for this property isn’t intended for enabling precise control over warehouse suspension.” Snowflake stops there and I carry on, so what follows is mine rather than theirs. If the poll phase is unrelated to when your last query finished, which the docs never say, the tail is 60 plus what is left of the poll: uniform between 60 and 90 for an average of 75, and g* moves right to 45 + 75 = 120. Those 15 seconds land on every cycle at 60 and once a sitting at 300, 15 ÷ 60 = 25% against 15 ÷ 5,245 = 0.3%, so the correction goes on 60 alone.

Work it through at Kettlebridge’s 80-second gap and it goes the wrong way, which is not where I expected to land. In the third of poll positions where the tail runs past 80 it never suspends and the next query is warm at 83 seconds; in the other two thirds it suspends after 70 on average and the next comes back cold at 70 + 48 = 118. Average them, (2 × 118 + 83) ÷ 3 = 106.3, and the penalty is 28% rather than 30%. Missing a suspend inside the window is a saving, so read the jitter as overhead and you have its sign backwards. The day follows: 9 × 60 × 106.3 = 57,402 seconds, 57,402 ÷ 3,600 = 15.95 hours, 15.95 × 8 = 127.6 credits against 129.6. The headline survives. The second decimal place does not.

Snowflake’s own docs recommend at least 10 minutes for BI warehouses

At least ten minutes is what Snowflake recommends for query warehouses, and the sentence sits four levels down its own Guides tree where almost nobody quotes it. The page is the one on optimising the warehouse cache, under performance optimisation; the section is “About the cache and auto-suspension”; the third and last bullet of its guidelines reads: “For query warehouses, for example BI and SELECT use cases, Snowflake recommends setting auto-suspend to at least 10 minutes to maintain the cache for users.”

A different page tells the same reader the opposite, and it is buried about as deep. Two-thirds of the way down the warehouse considerations page, under the subheading on automating warehouse suspension, Snowflake writes: “If you enable auto-suspend, we recommend setting it to a low value (for example, 5 or 10 minutes or less)”. Neither is on anybody’s first screen. They disagree, and 600 seconds — ten minutes — is the only value that actually satisfies both. 600 is also the shipped default, so a reader who opens neither page lands on the one number both agree about.

When a shorter auto-suspend is the right setting

Above the window, which is narrower than it sounds. A Snowflake warehouse that wakes on a schedule has gaps far outside any plausible cold-cache window, so every query arrives cold whatever the setting and every second of idle is a pure loss. Snowflake says so in the “Cost considerations” section of the same cache page: with a query every 30 minutes “it does not make sense to set the auto-suspend setting to 10 minutes.”

Price it on Kettlebridge’s numbers. Double the gap from 80 to 160 seconds, outside the 105-second window: 60 still bills 108 a cycle, 300 bills 3 + 160 = 163, and 108 ÷ 163 = 0.66, so the short setting is 34% cheaper. The answer reverses on one input.

Our piece on why a warehouse bill climbs on flat data drops auto-suspend to 60 seconds on a dashboard refreshing every fifteen minutes, and it is right to: 900 seconds is 900 ÷ 45 = 20 times this window’s width, another regime entirely. And the warehouse I asked you to hold on to sits in that bracket too. Snowflake’s SYSTEM$STREAMLIT_NOTEBOOK_WH runs its 60-second default timeout for notebook Python, while the cost note beside it sends “SQL queries from Notebook apps to a separate customer-managed query warehouse”. Snowflake’s one 60-second default sits on a warehouse it would rather you did not query.

Does auto-suspend matter on a busy warehouse?

No, and one row of the table above says why. Halve Kettlebridge’s gap to 40 seconds and neither setting fires: both bill 3 + 40 = 43 a cycle, and the parameter is inert at any value above 40. A steadily busy warehouse is the common case in accounts where people hunt for savings, and there auto-suspend is a distraction. The credits are in query shape and warehouse size. I’d spend the afternoon on the twenty most expensive statements instead, and say so on the call rather than after the invoice.

The cold-read penalty is the number you haven’t measured, so run the conclusion again on a smaller one. Kettlebridge’s is 48 - 3 = 45 seconds, chosen rather than observed. Halve it: the cold query becomes 25.5 seconds, the window narrows to 22.5 + 60 = 82.5, the 80-second gap is still inside, and 60 bills 85.5 against 83, so the conclusion holds by 3%. Cut it to a third: the cold query becomes 18, the window narrows to 15 + 60 = 75, the gap now sits outside, and 60 bills 78 against 83, which is 6% cheaper. The result survives a 2x derating and dies at 3x.

Two numbers you’d want aren’t here. A credit has no public price: the compute-cost page sends you to the Snowflake Pricing Guide for “the current price of a credit”, so every figure above stays in credits. The 8 credits an hour is Gen1 only, because the warehouse overview table says its numbers “refer to the first generation (Gen1) of Snowflake standard warehouses”.

Set it to 600, then, and go and look at something else, unless a measured cold-read penalty and a measured gap put you outside the window. 600 is the default, so for most warehouses the right move is actually to change nothing. Our data engineering work starts at the attribution query, never at the settings page.

Tell us what’s broken.

Twenty minutes, no deck. We’ll tell you straight whether this is work we should be doing, and if it isn’t, we usually know who should.

We come back within one business day.