<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neeraj Agarwal</title>
    <description>The latest articles on DEV Community by Neeraj Agarwal (@neeagl_algoscale).</description>
    <link>https://dev.to/neeagl_algoscale</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3886190%2Fe28bec7e-20bc-4399-b682-7969850c0559.jpg</url>
      <title>DEV Community: Neeraj Agarwal</title>
      <link>https://dev.to/neeagl_algoscale</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neeagl_algoscale"/>
    <language>en</language>
    <item>
      <title>Fabric OneLake shortcuts vs ADLS Gen2 mounts: what actually works in production</title>
      <dc:creator>Neeraj Agarwal</dc:creator>
      <pubDate>Sat, 18 Apr 2026 15:15:55 +0000</pubDate>
      <link>https://dev.to/neeagl_algoscale/fabric-onelake-shortcuts-vs-adls-gen2-mounts-what-actually-works-in-production-a3e</link>
      <guid>https://dev.to/neeagl_algoscale/fabric-onelake-shortcuts-vs-adls-gen2-mounts-what-actually-works-in-production-a3e</guid>
      <description>&lt;p&gt;If you've just moved a team onto Microsoft Fabric, the "how do I read our existing ADLS Gen2 data?" question comes up in the first week. Two patterns are on the table:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;OneLake shortcuts&lt;/strong&gt; - Fabric's newer, metadata-only references that make external data appear as a Lakehouse table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ADLS Gen2 mounts&lt;/strong&gt; - the &lt;code&gt;mssparkutils&lt;/code&gt;/&lt;code&gt;notebookutils&lt;/code&gt; pattern that's been around since Synapse&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The docs describe them as roughly interchangeable options. They aren't. We've now shipped both across ~15 Fabric projects and the failure modes are &lt;em&gt;completely&lt;/em&gt; different.&lt;/p&gt;

&lt;p&gt;Here's the field report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick refresher - the two patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Shortcut (the new way)
&lt;/h3&gt;

&lt;p&gt;Create via UI, REST API, or Fabric CLI. Zero-copy reference to an external location, exposed as a table or folder inside a Lakehouse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.fabric.microsoft.com/v1/workspaces/{wsid}/items/{lakehouseid}/shortcuts"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "path": "Tables",
    "name": "orders_raw",
    "target": {
      "adlsGen2": {
        "location": "https://prodstorage.dfs.core.windows.net",
        "subpath": "/raw/orders",
        "connectionId": "1234abcd-..."
      }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once created, Spark and SQL treat it as a normal Lakehouse table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lakehouse.orders_raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mount (the familiar way)
&lt;/h3&gt;

&lt;p&gt;In a Fabric notebook, create a logical mount path backed by an ADLS Gen2 container using a LinkedService:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;notebookutils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abfss://raw@prodstorage.dfs.core.windows.net/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders_raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linkedService&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prod_adls_linked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/synfs/nb_resource/orders_raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both read the same bytes. The differences start everywhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where shortcuts are magic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Direct Lake mode only works with shortcuts
&lt;/h3&gt;

&lt;p&gt;This is the big one. If you want your Power BI semantic models to query Lakehouse data with zero import latency via Direct Lake, the table &lt;em&gt;must&lt;/em&gt; live in the Lakehouse - which means shortcut or physical copy. Mounts don't count.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Direct Lake reads  →  Lakehouse table  →  shortcut OR Delta files in /Tables
Direct Lake reads  ✗  Mount path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Direct Lake is in your architecture diagram (and on Fabric it usually should be), this alone settles the argument.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ACLs inherit from the source
&lt;/h3&gt;

&lt;p&gt;Shortcuts honor the ACL model of the underlying storage. A user who can read &lt;code&gt;/raw/orders&lt;/code&gt; in ADLS via POSIX ACL or RBAC can read it through the shortcut - Fabric doesn't re-auth.&lt;/p&gt;

&lt;p&gt;Mounts run under the LinkedService's identity, which is usually a service principal with broad access. You've just given every notebook in that workspace the same blast radius.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Zero operational overhead at mount time
&lt;/h3&gt;

&lt;p&gt;Shortcut creation is metadata only. A 40TB dataset becomes queryable in under a second. Mounts require the runtime to resolve the path at session start - fine for small containers, annoying when you're re-starting sessions dozens of times a day across a team.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. SQL endpoint sees them
&lt;/h3&gt;

&lt;p&gt;A Lakehouse's SQL analytics endpoint exposes shortcut tables for free. Warehouses can cross-query them with three-part names. Mounts are invisible to the SQL endpoint - you can only read them from a notebook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Works on a shortcut, fails on a mount&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MyLakehouse&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;orders_raw&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_ts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where shortcuts silently break
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cross-region reads are expensive and slow
&lt;/h3&gt;

&lt;p&gt;Shortcuts don't copy data. If your ADLS account is in &lt;code&gt;westus2&lt;/code&gt; and your Fabric capacity is in &lt;code&gt;eastus&lt;/code&gt;, every query pulls bytes across the region boundary - at egress pricing, at cross-region latency.&lt;/p&gt;

&lt;p&gt;We had one client where a dashboard that ran in 4 seconds against a co-located Lakehouse took 38 seconds through a shortcut to a foreign-region ADLS. Nobody's documentation flagged this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; shortcut target and Fabric capacity home region should match. If they can't, plan for a daily materialized copy or move the capacity.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Connection credentials expire in ways you won't see
&lt;/h3&gt;

&lt;p&gt;When you create a shortcut via UI, Fabric prompts for credentials once and stores them as a Connection object. If that connection uses a SAS token, it expires. When it does, the shortcut starts returning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Error: [FabricOneLakeShortcutAuthFailure] Connection refused
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not at creation time. Not logged anywhere obvious. Users just see empty tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; use workspace identity or a managed-identity-backed connection, not SAS. If you must use SAS, alert on expiry explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. ADLS Gen1 is not supported
&lt;/h3&gt;

&lt;p&gt;We know, you thought it was fully deprecated. We had a client with a Gen1 account still humming along for a batch job. Fabric won't shortcut it - no workaround. You're migrating to Gen2 first.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Shortcuts to Hive-partitioned data don't auto-discover partitions
&lt;/h3&gt;

&lt;p&gt;If your external ADLS Gen2 layout is &lt;code&gt;/sales/year=2025/month=04/&lt;/code&gt;, a shortcut exposes it as a folder shortcut - not a partitioned table. You either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shortcut the root and read in Spark (&lt;code&gt;spark.read.option("basePath", …)&lt;/code&gt;) - loses the Lakehouse table abstraction&lt;/li&gt;
&lt;li&gt;Convert to Delta first on the ADLS side, then shortcut (the "right" answer, but it's work)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mounts have the same issue, but at least there's no expectation of table-like behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance we measured
&lt;/h2&gt;

&lt;p&gt;We ran the same Spark query - a 200M-row aggregation over Parquet sitting in ADLS Gen2 - through both access patterns, from a Fabric F64 capacity. Five cold runs, five warm runs, averaged:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Access path&lt;/th&gt;
&lt;th&gt;Cold (first run)&lt;/th&gt;
&lt;th&gt;Warm (cached)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delta files directly in Lakehouse &lt;code&gt;/Tables/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;11.2 s&lt;/td&gt;
&lt;td&gt;2.1 s&lt;/td&gt;
&lt;td&gt;Baseline. No shortcut, no mount.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shortcut to same-region ADLS (Delta)&lt;/td&gt;
&lt;td&gt;12.8 s&lt;/td&gt;
&lt;td&gt;2.4 s&lt;/td&gt;
&lt;td&gt;~15% overhead cold, negligible warm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mount to same-region ADLS (Parquet)&lt;/td&gt;
&lt;td&gt;14.1 s&lt;/td&gt;
&lt;td&gt;3.0 s&lt;/td&gt;
&lt;td&gt;Lacks Delta stats; CBO worse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shortcut to cross-region ADLS (Delta)&lt;/td&gt;
&lt;td&gt;38.4 s&lt;/td&gt;
&lt;td&gt;6.7 s&lt;/td&gt;
&lt;td&gt;Egress + latency dominates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same-region shortcuts are essentially free&lt;/li&gt;
&lt;li&gt;Cross-region shortcuts are a trap dressed up as convenience&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use which - decision matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Mount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direct Lake mode from Power BI&lt;/td&gt;
&lt;td&gt;✅ required&lt;/td&gt;
&lt;td&gt;❌ not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL analytics endpoint access&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User-level ACL enforcement&lt;/td&gt;
&lt;td&gt;✅ honored from source&lt;/td&gt;
&lt;td&gt;❌ runs as SPN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Iceberg tables (preview)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read ad-hoc files (CSV dumps, logs)&lt;/td&gt;
&lt;td&gt;⚠️ overkill&lt;/td&gt;
&lt;td&gt;✅ just mount and read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-region source storage&lt;/td&gt;
&lt;td&gt;❌ egress pain&lt;/td&gt;
&lt;td&gt;❌ egress pain - at least you know it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADLS Gen1&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write back to external storage from Fabric&lt;/td&gt;
&lt;td&gt;❌ shortcuts are read-focused&lt;/td&gt;
&lt;td&gt;✅ mount supports write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick ETL staging in a notebook&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;✅ simpler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The pattern we land on
&lt;/h2&gt;

&lt;p&gt;On every new Fabric engagement now we default to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;All production table access → shortcuts.&lt;/strong&gt; No exceptions. Direct Lake, SQL endpoint access, and source-ACL inheritance are non-negotiable in an enterprise deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shortcuts target same-region ADLS.&lt;/strong&gt; If source is elsewhere, we budget a one-way replication with a scheduled pipeline into same-region staging, then shortcut that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connections use workspace identity or MI-backed Connection objects.&lt;/strong&gt; SAS is banned unless there's a specific third-party constraint, and if it is, we alert on token expiry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mounts only for notebook-level ETL utility work&lt;/strong&gt; - reading a one-off CSV, writing a temp file back to a scratch container. Never for anything a dashboard depends on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ADLS Gen1 migrates to Gen2 before Fabric migration.&lt;/strong&gt; Non-negotiable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The TL;DR for teams coming from Synapse: you're going to reach for mounts because they're familiar. Resist. Shortcuts are the primitive Fabric is built around - learn them well, and most of Fabric's headline features (Direct Lake, SQL endpoint cross-query, cross-item data reuse) fall into your lap for free.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're mid-migration and want a second set of eyes on shortcut architecture, the team at &lt;a href="https://algoscale.com/data-lake-consulting-services/" rel="noopener noreferrer"&gt;Algoscale&lt;/a&gt; runs these assessments regularly. The full write-up of the migration playbook including the connection-identity pattern lives &lt;a href="https://algoscale.com/go/blog/fabric-shortcuts-vs-adls-mounts/" rel="noopener noreferrer"&gt;on our blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>azure</category>
      <category>tutorial</category>
      <category>microsoft</category>
    </item>
  </channel>
</rss>
