How Much Does Direct Lake Cost in Microsoft Fabric?
- Vojtěch Šíma
- 1 day ago
- 10 min read
Updated: 7 hours ago
tl;dr Direct Lake seems to be cheaper than Import, primarily because of Import's refresh, which is absent in Direct Lake, at least in the same form. Direct Lake has somewhat more expensive DAX queries and has additional overhead that may revert some of the freed cost. A properly set up Import with Incremental refresh can still beat both of them and is a valid alternative. In general, when you measure Direct Lake cost, don't forget to count in the Lakehouse/Warehouse operations tied to Direct Lake, and be careful when deploying Direct Lake on smaller capacities due to warm-up burst cost. More in the blog.
Disclaimer: If not said otherwise, all measurements are being done on Trial F64 and Direct Lake on One Lake. All terms I will use can be found explained together with whole Direct Lake here.
Direct Lake cost in terms of Capacity Units (CU)
Direct Lake is cool, but did you measure how much it costs? And cost in this blog means how many Capacity Units it consumes. I already produced some numbers in my previous blog, where I talk about Direct Lake in detail, but those numbers were a small snippet of a particular operation; however, to produce overall reliable numbers, I had to dig a bit deeper.
So in this blog, you should get an idea of what Direct Lake can cost and how it compares to Import. Note that exact numbers are not really that relevant without context of how it's used, so for every claim I also state the environment where it was measured. However, one thing that will likely stand on its own is the percentage difference between different settings and options.
Capacity unit (CU), or precisely Capacity unit second (CU-s), is the base measurement unit to track consumption of Microsoft Fabric capacity. The math is pretty straightforward here. Depending on your Fabric SKU, let's say F2, you'll have 2 CU per second. Therefore in a month you have the seconds in a month multiplied by 2. Therefore, your capacity for a month could be 5 million CU-s. How you spend it, it's on you.
Direct Lake Cost Buckets
I am gonna tease you a bit before revealing everything, but if you just want the numbers, scroll ahead; I don't mind. So, if you're still reading this, I wanted to discuss what contributes to Direct Lake cost more precisely, because it's not just one item.
The buckets are:
Refresh / Framing
Warm up and Querying
Storage Item Cost
The first, in chronological order, since it's the first thing that happens, is Refresh, or in Direct Lake's terms, Framing. That's how the Delta table's data gets in. By default, this happens automatically and costs a tiny amount of CU-s, tiny meaning anything from 10 to 17 CU-s, out of 5 million on the lowest capacity; that's tiny.
Now the second, and the biggest. These two things are essentially the same thing under the hood. Warmup is the process that loads columns into VertiPaq and lets you actually read or display any visuals. This is the most expensive part of Direct Lake, at least on a good model. To give you an idea, a simple semantic model with 5 tables, each around 10 columns, and a fact table of 12 million rows, can cost 650 CU-s. This number can go lower, but also way higher, note that this is a mostly well-optimized model.
Finally, the most frequent thing you'll do is Querying. In other terms, looking at visuals in your report. I say querying because that's what happens: you send DAX Queries to the storage to get the data for your visuals. In my tests, I wasn't really looking at pictures, but was bursting queries like I was a report user. And here the numbers can be literally anything, but this will most likely be the biggest price tag, and it also depends on how you design your DAX, your model, how many users you have, how active they are, etc. In my setup, around 400 queries cost 1400 CU-s. Don't worry, I'll give you a table that's easily read later.
Finally, the cost that's perhaps a bit hidden, and it kind of is, because you won't see it when you only monitor the semantic model. The storage item cost. Here, storage item means Lakehouse or Warehouse. A Direct Lake model introduces an overhead for the storage item. This practically depends on whether you have "Keep your Direct Lake data up to date" on or not. If you don't, there's no additional cost, because this operation is the 'sync' to get fresh data. If you do have this feature on, which is by default, you'll get charged around 0,624 CU-s per 30 seconds, while your Semantic Model is active. The active part is tricky, and I'll try to explain it further in the next section.
Keeping your Direct Lake Data Up To Date
As I mentioned, Direct Lake introduces an overhead on the storage item (Lakehouse) when you chain a Direct Lake semantic model to it. This overhead is charged through the Lakehouse/Warehouse, and the cost is per model. Let's do a quick math. If you have a single model that's being active for 24 hours, that's 0,624 per 30 seconds; that's 1,248 CU-s a minute, 74,88 CU-s an hour, 1797 CU-s a day. For ten models, that's 17971. From the daily F2 capacity, that is around 10% of the daily capacity only for that sync.
Whether this is a lot or not depends on your use cases. If you pull data once a day, that's a lot of wasted CUs. But at that point, you perhaps don't need Direct Lake. If you need to update your data fifty times a day, that's probably a better bargain.
However, this cost is not always there, just because you bound a Semantic Model to a Lakehouse. I mentioned something about an 'Active' semantic model, so let's explain it.
In my testing, I thought an 'active' semantic model would be a model that has resident column segments. Which I found is not true, and it primarily lies on the framing. As I said, if you disable the option of keeping Direct Lake data up to date, there's gonna be zero sync and you have no 'active' semantic model. If you have it on, you'll get charged when your semantic model is framed, regardless of whether anything is loaded in memory. The sync does stop on its own eventually, and it can be forced to stop by a Clear Values refresh. If you clear values, you lose residency and also the framing, and the sync stops. If you were to immediately reframe it after, and you wouldn't even query the model, the sync will resume.
Direct Lake vs Import CU consumption
Okey. This is what you probably came for. Lemme first introduce the testing environment, so you get some context and you can re-apply it to your own scenario.
Method and Environment
I call this test "9 to 5". Essentially, I track approximately an 8-hour workday. I ran it simultaneously on F64 and F2, with 9 semantic models in total, 7 of them on the F64, two on the F2.
Each test starts with a full refresh for Import, a full warmup for Direct Lake, and a partial refresh for the Incremental Import. Each model is fairly equal: 5 tables, 1 fact table of 12 million rows, optimized Parquets, and a healthy model with good practices.
In those 8 hours, it spat out around 380 queries, of predefined shapes with changed filters, simulating browsing of a report's pages. The queries mimic simple card visuals, tables, or matrixes. It was always only a single 'user'. There was also a one-hour lunch break, where all the models stopped, partly to see if a break causes any potential worsening of performance for Direct Lake, something Import would be fairly immune to.
I ran 9 replications of this test; that means in total approximately 71 hours worth of testing on the 9 models at the same time, 30 780 queries in total.
I did a lot of replications and long runs to even out certain inequalities that can happen, such as some fast queries not even being billed, or not producing a record in Capacity Metrics, which often happened on Import. The overall numbers, however, were quite consistent.
One note on the setup: all the models on the Lakehouse were sharing the same one. I did some controlled solo tests too, but more to check for anomalies than as the main measurement. The Lakehouse itself was quite crowded with other tables and other stuff, though during the tests nothing else was running on it, but everything read the same set of five tables. In general, the Lakehouse had way more data sitting in it than the Warehouse counterpart. That's just for context on the results below, since the Warehouse appears to come out a bit better. I don't want to claim it's because of the different level of "pollution" on the two sources, but I wanted to note that I didn't have a fully clean setup; maybe next time.
But again, all the tables were equal, with the same predefined set of queries.
Result
Results are medians across the 9 replications. Day range shows the individual volatility. All results are CU-seconds.
F64
Model | Setup or refresh | Queries | Day total | vs baseline | Day range |
Import, full refresh | 2 412,6 | 1 004,2 | 3 541,9 | baseline | 3 255 - 4 865 |
Import, incremental | 1 030,8 | 475,6 | 1 522,6 | -57,0% | 1 183 - 2 250 |
Direct Lake on SQL endpoint | 685,8 | 1 576,3 | 2 241,4 | -36,7% | 1 802 - 3 186 |
Direct Lake on OneLake | 642,5 | 1 548,5 | 2 191,0 | -38,1% | 1 724 - 2 866 |
Direct Lake on SQL, fallback off | 646,1 | 1 463,5 | 2 103,2 | -40,6% | 1 904 - 3 301 |
Direct Lake on OneLake, Warehouse source | 318,0 | 1 416,2 | 1 748,0 | -50,6% | 1 321 - 1 950 |
Direct Lake on SQL, Warehouse source | 183,5 | 1 299,5 | 1 527,3 | -56,9% | 1 025 - 2 064 |
F2
Model | Setup or refresh | Queries | Day total | vs F2 baseline | Day range |
Import F2 | 2 670,5 | 971,6 | 3 574,3 | baseline | 3 050 - 3 925 |
Direct Lake on OneLake F2 | 362,3 | 1 172,6 | 1 521,0 | -57,4% | 1 265 - 2 018 |
Numbers are measured directly on the semantic models. We can also add the direct cost of storage items that are connected to the semantic models:
F64
Source | Refresh | Sync | Total |
Lakehouse | 0 | 2 826,7 | 2 826,7 |
Lakehouse SQL endpoint | 669,2 | 0,0 | 669,2 |
Warehouse | 250 | 1 320,2 | 1 570,2 |
Source side total | 919,2 | 4 146,9 | 5 066,1 |
These numbers are aggregated per run for all noted models above. Refresh is for Import models, Sync for Direct Lake.
Refreshes are not equal
Without any context, it seems that Direct Lake is cheaper on total consumption than Import. And that's primarily because of the absence of Import's refresh.
The refresh of the Import semantic model itself is probably the most expensive operation in Fabric (now I am not talking about fancy new features that could come); if you are able to pull the refresh duration down, it's gonna be cheaper. In my own scenario, the refresh itself was fairly short. That's approximately 2 minutes. If the refresh is averaging around 24 CU-s for every second of refresh, if my refresh took 15 minutes, it wouldn't be 2880 CU-s total, but 21,6k CU-s. Then the saving on Direct Lake would be way better.
Although not all Import refreshes are equal. If you do an incremental refresh, you can achieve faster refresh and technically faster queries if you hit some sweet spot in the partitioning department. That's one optimization you can probably do right now. And you don'T need to invest time into Direct Lake.
One note, during Import refresh, you can also hit the SQL Analytics Endpoint of your Lakehouse. So you have to add that cost as well. Naturally this depends on what your source is or not, but it's worth knowing, and in this specific case it can technically be bypassed with another connector, if you don't need SQL query.
Queries
The DAX queries themselves are also slightly different, Import has definitely cheaper queries even when they should technically work the same and use the same engine behind it; however, it seems that Capacity Metrics is adding a bit of a margin to it, or Direct Lake hides some additional work to it. So what you lose in Import on Refresh, you can slowly gain back on Queries. However, you would probably have to do a lot of querying to break even.
Capacity units alone don't equal Capacity Utilization
The CU cost came out the same on both capacities; that's a good sign. However, the Capacity units raw numbers don't tell the full picture. There's something called Capacity Utilization %, which is usually measured at a point in time, to tell you whether your capacity is healthy or it may start throttling, etc. If you would just divide the total of CU consumed against your daily total capacity, you would say, "I am all right, still have 40% left?" However, what's extremely important is how fast you consumed the 60% of the capacity.
There is simple math behind it (again). The lowest timepoint Capacity Metrics measures is 30 seconds, and the limit in that window is 30 * SKU. For F2 that's 60 CU-s. Hit it, and you're at 100%, and things can go south soon.
There are different types of operations, like Interactive and Background.
Background gets spread over a much longer window than Interactive. Refresh, one of the activities, may not kill your capacity, just because it costs a lot of CUs. On the other hand, the Interactive may; those are reading visuals or warming up Direct Lake.
Why am I mentioning it?
Metric | Import F2 refresh | Direct Lake F2 warm-up |
CU-s | 2 381 | 317 |
Type | background | interactive |
Peak it added | 1,4% | 52,9% |
Because, as you can see in the table, even though the warm-up cost a fraction of the full refresh, it was packed inside a tiny window that put the utilization into over 50%, having two of those models, I would most likely be bursting.
So when you start experimenting, note that with overall CUs you should also think about how those will be deployed in time, to stress out your capacity evenly.
Overall conclusion
If you should take something from this article, Direct Lake can be worth it and can be cheaper, but you have to make sure you add up all the numbers, especially the sync and the warm-up. You can always disable the "Keep your Direct Lake data up to date" for each model and do programmatic tiny refreshes-framing "manually". Depends on your use cases.
Naturally, with a good setup, good old Import can still be a beast and definitely should not be automatically discarded in favor of Direct Lake. Lastly, if you're on F2, I don't recommend putting a report on the capacity in general, but if you have something small that will utilize Direct Lake's strengths, why not.


Comments