Direct Lake, deployment pipelines, and per-BU shortcuts on one capacity
Adventure Works runs a single F64 Fabric capacity in West Europe. Lakehouse_DEV and Lakehouse_PROD hold the same Delta tables: gold_sales, gold_inventory and gold_hr. A nightly Spark notebook writes the gold tables in DEV, and a deployment pipeline promotes the lakehouse plus one Direct Lake semantic model, SalesModel, to PROD. Three business-unit workspaces (Sales, Inventory, HR) each contain a lakehouse whose only item is an OneLake shortcut to the matching gold_ table in Lakehouse_PROD. Each BU has its own app over SalesModel, filtered by RLS.
After the most recent promotion: SalesModel in PROD returns DEV values; every visual touching gold_hr falls back to DirectQuery and takes about 40 seconds; HR analysts get a table-not-found error when they open their shortcut; and Capacity Metrics shows interactive delay every weekday at 09:00.
1. PROD shows DEV numbers and gold_hr is slow. State the two most probable causes, the verification step for each, and the fix that keeps a single model definition.
Read the worked answer
SalesModel returns DEV data. A Direct Lake model stores an OneLake artifact path as its data source. Deployment pipelines re-point that path only when the model and the lakehouse it was bound to are deployed together in the same pipeline with lineage recorded. If Lakehouse_PROD was created by hand, or the model was deployed in a separate run, the model keeps the DEV path.
Verify: open the deployed model's Settings → Parameters and read the OneLake/lakehouse path; then check in the pipeline whether the lakehouse and model show the same lineage group.
Fix: rebind the model in PROD, and keep lakehouse and model in one pipeline stage so auto-binding happens on every promotion. Do not hand-edit the model in PROD afterwards or the binding drifts again.
gold_hr falls back to DirectQuery. Direct Lake frames tables from Delta. If the shortcut behind gold_hr resolves to CSV/Parquet or any non-Delta artifact, or the table carries column types the engine cannot load, that table is not framed and the query is served by DirectQuery.
Verify: read the Delta transaction log of the target, or check the shortcut target format. Then confirm with Performance Analyzer that gold_hr is the only DirectQuery-served table.
Fix: land gold_hr as Delta (convert the source or materialise a Delta copy), keep the shortcut if you want the indirection.
2. HR analysts get table-not-found. Explain the mechanism and design an isolation scheme under the requirement that no BU can read another BU's gold table.
Read the worked answer
A OneLake shortcut is a pointer, not a copy and not a permission grant. When HR opens their shortcut the request is evaluated against the target item in Lakehouse_PROD. HR users have Viewer on their own workspace only, so they have no read on Lakehouse_PROD - the shortcut resolves to nothing they can access. Adding them to PROD as Viewers would also expose gold_sales and gold_inventory, so it does not meet the requirement.
Options, in ascending order of strength:
- RLS in a semantic model only. Fast, but it protects the model, not OneLake. Anyone with read on PROD can still open the tables directly. Insufficient alone.
- Separate gold lakehouses per BU in PROD, each with item-level permissions and a shortcut from the BU workspace. Cheap and explicit; the lakehouse SQL analytics endpoint has no granular object security, so separation must happen at item level.
- Gold as a Warehouse with T-SQL GRANTs on schemas/views per BU, plus column-level or row-level security in T-SQL. Only choose this if you also need the Warehouse's DML story.
Recommendation: option 2 for the lakehouse-shaped estate; keep one Direct Lake model over the union only if row-level security is genuinely enforceable, and never rely on a shortcut as the boundary.
3. At 09:00 reports are delayed. Average capacity utilization is 45% with two nightly peaks of 190% lasting 20 minutes. What are the metrics actually telling you, and how would you rank the remediations?
Read the worked answer
Average utilization is the wrong indicator. Fabric evaluates consumption over short time windows, smooths the 24-hour total, and compares it against the SKU. Overage beyond 100% for a sustained period triggers delayed interactive operations, then rejected interactive operations, and eventually rejected background operations. A 190% burst does not simply average away: it creates carryover that is deducted from later windows, so the 09:00 slowdown can be the interest on a 04:00 burst.
Ranking:
- Attack the peak. Find the top item in the Capacity Metrics app during the burst. Typically one model refresh plus three concurrent Spark sessions. Staggering schedules and making the notebook incremental usually removes the peak at zero cost.
- Reduce the work per burst. If the 40-second gold_hr query is DirectQuery, fixing that (previous answer) removes both a user-visible wait and serverless query CU.
- Scale up or autoscale. F64 to F128 doubles the budget and buys time, but it does not fix a single-threaded 40-second query or a badly scheduled overlap, and it doubles the bill. Treat as a follow-up, not a first move.
Order matters: scaling first hides the design problem and you will be back at 09:00 delays at the higher SKU within a quarter.