Microsoft Fabric capacity: sizing, smoothing, and throttling

The report was slow. Not broken, not erroring — slow, in the specific way that makes users refresh and make it worse. It had been fine the previous week. Nothing about the semantic model had changed, nothing about the data volume had changed, and the person who owned the report had no visibility into what had: a data scientist in a different workspace had started running notebook experiments on the same capacity, three floors away, with no idea that "capacity" was a thing you could share.

That incident is the whole subject of this article, because it comes from one design decision that Microsoft Fabric makes and most cloud data platforms don't. Fabric does not bill you for queries. It bills you for a capacity — a fixed pool of compute you rent by the hour — and then meters your work against it in capacity units, smoothed over time. Every cost instinct from a per-query warehouse is wrong here, and almost every Fabric surprise I've been called about traces back to not internalizing that sentence.

The mechanism, in one pass

You buy an F SKU — F2, F4, F8, and up, where the number is roughly the capacity units available per second. Everything that runs on the platform (a Power BI query, a Spark notebook, a pipeline copy, a warehouse query, an eventstream) consumes CUs from that pool. When the pool is exhausted, work doesn't fail immediately; the platform applies smoothing, and then, if you keep going, throttling.

Three concepts do all the work:

ConceptWhat it doesWhy it matters to you
BurstingA single operation may use more CU than your SKU nominally provides, to finish fasterYour notebook isn't capped at the SKU rate — it can go faster and borrow against the future
SmoothingThe CU cost of an operation is spread over a following window rather than charged instantlyThis is why a spike doesn't kill you — and why yesterday's spike can throttle you today
ThrottlingWhen smoothed future usage exceeds what you've bought, the platform delays then rejects workThe user-visible symptom: reports get slow before anything gets rejected

Smoothing is the piece worth understanding properly, because it's the one that makes Fabric behave unlike anything else. A heavy background job doesn't consume its cost in the ten minutes it ran — that cost is spread forward across a window measured in hours. Which means the throttling you experience at 9am can be the debt from a job that finished at 2am, and looking at what's running right now will tell you nothing about why you're being delayed. This is the single most confusing thing about the platform and the reason the capacity metrics app is not optional.

Interactive operations (a user waiting on a report) and background operations (a scheduled refresh, a pipeline) are smoothed differently, with background work spread over a longer window. That distinction is deliberate and useful: it's what lets a nightly batch coexist with a business-hours reporting workload — as long as the batch doesn't run so hot that its smoothed debt is still being paid when the business logs in.

graph TB
  WL["Workloads: reports · notebooks
pipelines · warehouse queries"] --> CU{"CU consumption"} CU -->|"one operation may exceed
the SKU rate"| BURST["Bursting
finish faster now"] BURST --> SMOOTH["Smoothing
cost spread over a forward window
(background spread longer than interactive)"] SMOOTH --> BUDGET{"Smoothed future usage
vs what you bought"} BUDGET -->|"within budget"| OK["Everything normal"] BUDGET -->|"modest overage"| DELAY["Interactive DELAY
reports get slow — the first symptom"] BUDGET -->|"sustained overage"| REJECT["Interactive REJECTION
+ background rejection"] DELAY --> USER["Users refresh
making it worse"] NIGHT["2am batch job"] -.->|"smoothed debt
still being repaid"| BUDGET NIGHT -.-> MORNING["9am: reports slow,
nothing is running"]

The dotted path is the one that generates support tickets. Because background cost is smoothed forward over hours, the capacity you are short of this morning may have been spent by a job that finished overnight — so "what's running now?" is the wrong question and the metrics app's timeline view is the right one. The other trap is on the left: users respond to delay by refreshing, which adds interactive load to a capacity that is already over budget.

Sizing an F SKU without guessing

The instinct is to size for peak. That's expensive and usually wrong, because bursting and smoothing exist precisely to absorb peaks. What you're actually sizing for is sustained average consumption, with enough headroom that your peaks are repaid before the next one arrives.

The method I use:

  1. Measure before you commit. Run a representative fortnight on a pay-as-you-go capacity — one that includes a month-end if your month-end is heavy. Guessing from user counts produces bad answers because two organizations with the same headcount can differ tenfold in refresh behaviour.
  2. Read the timeline, not the average. In the capacity metrics app, look for periods where smoothed usage approaches 100% and how long it stays there. A capacity that touches the ceiling briefly is correctly sized; one that sits at the ceiling for hours is under-sized and already delaying someone.
  3. Separate the question of peak size from peak frequency. A single monthly monster job is a scheduling problem (run it when nothing else needs the capacity); a daily 4pm spike that collides with reporting is a sizing problem or a workspace-separation problem.
  4. Then choose reservation versus pay-as-you-go. Reserved capacity is materially cheaper for a baseline you know you need; pay-as-you-go, which you can pause, is right for the variable part and for non-production. Most estates want both, which is an argument for splitting capacities rather than buying one large one.

Pause, and the thing everyone forgets

A pay-as-you-go capacity can be paused, and a paused capacity does not bill. For development and test capacities this is the single biggest lever available — a dev capacity running 24/7 for a team that works business hours is roughly three quarters wasted. Automate pause and resume on a schedule.

The thing everyone forgets: pausing settles the smoothed debt. Work you'd spread over the following hours is billed at pause time rather than quietly vanishing, which is correct and occasionally an unpleasant surprise for someone who paused right after a heavy job expecting to escape the cost. It's not a loophole; it's an accounting boundary.

Workspace and capacity design

This is where the incident I opened with gets prevented, and it's a design decision rather than a setting.

A capacity is a blast radius. Everything assigned to it competes for the same CUs, so the question "who can slow down my reports?" has exactly the answer "everyone on this capacity." The patterns that work:

  • Separate production reporting from experimentation. If business-critical dashboards and ad-hoc data science share a capacity, the data scientists will eventually — innocently — degrade the dashboards. Two capacities cost more than one and are cheaper than the incident.
  • Separate by SLA, not by team. Org-chart-shaped capacity assignment feels natural and produces a capacity holding one critical report and forty experiments. Group by how much a delay hurts.
  • Keep dev and test on pausable capacities, and pause them.
  • Watch the cross-capacity gotcha: a report in one workspace reading a semantic model in another consumes CU where the model lives, not where the report does. Cost attribution surprises usually start here.

The FinOps framing I'd apply — visibility, attribution, optimization, accountability, as in FinOps for data platforms — maps onto Fabric with one wrinkle: because the bill is flat, optimization doesn't reduce your invoice until you resize or pause. It buys you headroom instead. That's a genuinely different conversation to have with a sponsor than the per-query one, and worth having explicitly.

Three ways teams get hurt by the capacity model. Sizing on peak and never revisiting. An F64 bought "to be safe" is a large standing bill for headroom you may not need once you've moved two jobs off business hours — and nobody ever schedules the review. Treating throttling as a platform fault. The slow-report symptom gets escalated as a Power BI problem, and the investigation goes to the semantic model rather than to the capacity timeline, which is where the answer is. If reports are slow and nothing about them changed, look at capacity before you look at DAX. Letting one workspace hold everything. It's the default path, it's convenient, and it means your quarterly-close refresh and someone's exploratory notebook are in the same queue. The fix is architectural and cheap to do early, expensive to retrofit once forty workspaces exist. Add a fourth if you're on a reserved SKU: reservations don't autoscale, so a capacity you outgrow mid-term becomes a throttling problem you can't buy your way out of quickly — leave headroom or keep a pay-as-you-go capacity alongside for overflow.

What to monitor

The Fabric Capacity Metrics app is the only view that answers the questions that matter, and installing it should be part of standing up any capacity rather than something you reach for during an incident. Three things to look at regularly:

  • The utilization timeline, not the point-in-time number — specifically how often and for how long smoothed usage reaches the ceiling.
  • Throttling events, broken out by delay versus rejection. Any interactive delay means a user waited because of capacity, which is the earliest real signal you get.
  • Top consuming items over a window. This is where you discover that one poorly-partitioned notebook or one over-frequent refresh is a third of your consumption — and it's usually a five-minute fix once you know.

Set an alert on sustained high utilization rather than on spikes. Spikes are what bursting is for; it's the plateau that means someone is waiting.

What to carry away

Fabric rents you a capacity and meters everything against it, so bursting, smoothing and throttling are the three mechanics that explain every surprise on the platform. Smoothing in particular means cost is spread forward, so the throttling you feel this morning may be debt from a job that finished overnight — which makes the capacity metrics timeline, not the list of running queries, the place to investigate.

Size for sustained average with headroom, measured on a real fortnight rather than estimated from headcount, and split the decision between a reserved baseline and pausable pay-as-you-go for the variable and non-production parts. Pause dev and test on a schedule, and remember that pausing settles smoothed debt rather than escaping it.

Above all, treat a capacity as a blast radius and separate workloads by how much a delay hurts, not by which team owns them. The report that got slow because someone three floors away started a notebook is not a Power BI problem, a DAX problem, or a data problem — it's a capacity design problem, and it's the cheapest one on this list to prevent and the most annoying to explain after the fact.