<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Databricksters]]></title><description><![CDATA[Field-tested Databricks solutions from specialists who deployed them]]></description><link>https://www.databricksters.com</link><image><url>https://substackcdn.com/image/fetch/$s_!zPJJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff49ecae-7c56-403c-9389-61b28de6a50f_1280x1280.png</url><title>Databricksters</title><link>https://www.databricksters.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 05 Aug 2026 20:41:04 GMT</lastBuildDate><atom:link href="https://www.databricksters.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Soni]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[databricksters@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[databricksters@substack.com]]></itunes:email><itunes:name><![CDATA[Canadian Data Guy]]></itunes:name></itunes:owner><itunes:author><![CDATA[Canadian Data Guy]]></itunes:author><googleplay:owner><![CDATA[databricksters@substack.com]]></googleplay:owner><googleplay:email><![CDATA[databricksters@substack.com]]></googleplay:email><googleplay:author><![CDATA[Canadian Data Guy]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Making UC Metric Views Discoverable for Agents]]></title><description><![CDATA[Why does a semantic layer matter?]]></description><link>https://www.databricksters.com/p/making-uc-metric-views-discoverable</link><guid isPermaLink="false">https://www.databricksters.com/p/making-uc-metric-views-discoverable</guid><dc:creator><![CDATA[Chaman Jalan]]></dc:creator><pubDate>Wed, 29 Jul 2026 15:01:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fn3V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1><strong><span>Why does a semantic layer matter?</span></strong></h1><p style="text-align: justify;"><span>Every company depends on a handful of KPIs: revenue, active users, churn, margin, and each one usually gets defined in a dozen places at once: a SQL query here, a spreadsheet formula there, a dashboard filter somewhere else, each subtly different, until two teams walk into a meeting with two revenue figures and no one can say which is right. A semantic layer fixes that by providing a single governing definition that every tool reads, and </span><a href="https://docs.databricks.com/aws/en/business-semantics/metric-views"><span>Unity Catalog Metric Views</span></a><span> are Databricks&#8217; version of it.</span></p><p style="text-align: justify;"><span>That consistency has always mattered, but AI raises the stakes. When an agent answers a business question at machine speed, a wrong or ambiguous definition no longer just misleads one analyst; it gets baked into automated decisions and customer-facing answers. The semantic layer is where trust in your data is enforced, and your agents are only as good as the definitions behind them. This post is about the newest consumer of that layer, the AI agent, and a lightweight pattern that lets it reach the semantic layer using only existing Databricks services.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong><span>Agents can&#8217;t find the </span></strong><em><strong><span>right</span></strong></em><strong><span> metric definition</span></strong></h2><p style="text-align: justify;"><span>A person finds the right metric view without thinking about it. An agent starts from nothing. When a custom agent receives a question like </span><em><span>&#8220;what was our average order value this quarter?&#8221;</span></em><span>, it first has to find the right metric view from among hundreds across dozens of domains, and then understand its measures, dimensions, and query syntax.</span></p><p style="text-align: justify;"><span>Today, there&#8217;s no first-class, public way to search across them. An agent can already reach the definitions: it can enumerate catalog objects and read a metric view&#8217;s definition through the Unity Catalog REST API or SQL, and run one with the MEASURE() function. The trouble is choosing. Enumerating and parsing raw definitions on every call doesn&#8217;t scale past a small catalog. It re-scans each time, burns tokens, and surfaces overlapping or conflicting definitions with no way to rank them.</span></p><p style="text-align: justify;"><span>The semantic layer is a valuable, governed asset, but there&#8217;s no discovery surface that lets an agent ask, &#8220;Given this question, which metric view should I use, and what does it expose?&#8221; the way it would for any other object it can reach. This post builds that surface.</span></p><h2><strong><span>Expose the semantic layer as MCP</span></strong></h2><p style="text-align: justify;"><span>The idea is a small, customer-owned Databricks App that acts as an MCP server with a single job: answer </span><em><span>&#8220;which metric views match this ask, and how do I query them?&#8221;</span></em></p><p style="text-align: justify;"><span>At startup, the app builds an in-memory index of trimmed metric-view metadata: each view&#8217;s name, description, measures, and dimensions. It exposes one MCP tool, search_metric_views. An agent calls it with a natural-language ask and gets back the matching metric views, their measures and dimensions, and an example query so the agent knows how to use them without having to learn the metric-view syntax on its own.</span></p><p style="text-align: justify;"><span>Two design choices matter:</span></p><ul><li><p style="text-align: justify;"><strong><span>It&#8217;s discovery, not execution.</span></strong><span> The app returns </span><em><span>which</span></em><span> metric views fit and an example of how to query them.</span></p></li><li><p style="text-align: justify;"><strong><span>You control what&#8217;s exposed.</span></strong><span> The app can index only </span><em><span>certified</span></em><span> metric views, so external agents see governed, production-ready definitions and nothing that&#8217;s still in development or QA.</span></p></li></ul><h2><strong><span>Architecture</span></strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fn3V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fn3V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 424w, https://substackcdn.com/image/fetch/$s_!fn3V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 848w, https://substackcdn.com/image/fetch/$s_!fn3V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 1272w, https://substackcdn.com/image/fetch/$s_!fn3V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fn3V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fn3V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 424w, https://substackcdn.com/image/fetch/$s_!fn3V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 848w, https://substackcdn.com/image/fetch/$s_!fn3V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 1272w, https://substackcdn.com/image/fetch/$s_!fn3V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4777d5-bd5f-49f2-b196-cb695caba028_1774x887.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: justify;"><span>Multiple agents, inside or outside Databricks, connect to one Databricks App, which acts as the MCP server and looks up the semantic layer in Unity Catalog. The pattern improves discoverability: the app returns matching metric definitions and an example query. The agent can take necessary actions to generate SQL or manage the semantic layer to avoid duplication, etc., based on the use case.</span></p><p style="text-align: justify;"><strong><span>Where does the metadata live?</span></strong></p><p style="text-align: justify;"><span>The index can live in a few places, and each earns its place in a different situation:</span></p><ul><li><p style="text-align: justify;"><strong><span>In-memory, inside the app</span></strong><span>: the simplest option, and the one this post uses. No extra infrastructure, and reads are fast because they never leave the process. The catch is that the index rebuilds on every restart; each running copy of the app maintains its own copy; definitions can go stale between refreshes; and search is at the keyword level. Enough for a bounded catalog and a single team.</span></p></li><li><p style="text-align: justify;"><strong><span>Metadata table via a SQL endpoint:</span></strong><span> fits a bounded catalog that changes rarely. Every app instance reads one shared, governed copy, refreshed by a scheduled job.</span></p></li><li><p style="text-align: justify;"><strong><span>Lakebase</span></strong><span>: fits when many app instances and agents hit the index at once, and you want durable, low-latency lookups with structured filters such as domain, owner, or certified status, with room to keep usage or audit state alongside it.</span></p></li><li><p style="text-align: justify;"><strong><span>Managed AI Search (vector) index</span></strong><span>: fits when search quality is the point, whether a large catalog or plain-language questions, where ranking by meaning beats keyword matching, at the cost of more infrastructure and an index to keep in sync.</span></p></li></ul><p style="text-align: justify;"><span>The right choice comes down to the size of the catalog, the number of requests per minute you expect, and the amount of latency you can tolerate.</span></p><h2><strong><span>Governance</span></strong></h2><p style="text-align: justify;"><span>The app runs under a service principal, and you govern what it can reach with Unity Catalog grants (for example, restricting it to certified metric views). This fits multi-agent and cross-environment scenarios where the calling agents don&#8217;t carry a Databricks user identity.</span></p><p style="text-align: justify;"><span>If you need user-level access control, on-behalf-of (OBO) user authorization is supported. The agent acts as the calling user, and row and column governance is preserved end-to-end. It adds identity-passing complexity and isn&#8217;t required for the service-principal pattern this post uses.</span></p><h2><strong><span>Prerequisites</span></strong></h2><p><span>Before you build the app, get a few things in place:</span></p><p><span>1. Databricks workspace</span></p><ul><li><p><span>Active Databricks workspace</span></p></li><li><p><span>One or more Unity Catalog metric views</span></p></li></ul><p><span>2. An MCP-capable agent or client: anything that speaks the Model Context Protocol can connect and call the search tool.</span></p><h1><strong><span>Setup Instructions</span></strong></h1><blockquote><p><span>Step 1: Get the code</span></p><p><span>Download the five files from the repo folder:</span></p><p><a href="https://github.com/adgitdemo/ad_databricks/tree/main/mcp-metric-view-search"><span>https://github.com/adgitdemo/ad_databricks/tree/main/mcp-metric-view-search</span></a></p><p><span>You&#8217;ll have app.py, app.yaml, config.yaml, requirements.txt, and README.md.</span></p><p><span>Step 2: Point it at your warehouse and your data</span></p><p><span>Two files need real values before you deploy.</span></p><p><span>In app.yaml, set your SQL Warehouse ID. You&#8217;ll find it under SQL Warehouses &gt; your warehouse &gt; Connection details.</span></p><p><em><span>env:</span></em></p><p><em><span>  - name: DATABRICKS_WAREHOUSE_ID</span></em></p><p><em><span>    value: &#8220;your_warehouse_id&#8221;</span></em></p><p><em><span>In config.yaml, specify which catalogs and schemas the app should scan for metric views. Scope it down to keep the index focused, or leave it empty to scan everything the app can see.</span></em></p><p><em><span>scope:</span></em></p><p><em><span>  - catalog: your_catalog</span></em></p><p><em><span>    schemas:</span></em></p><p><em><span> - your_schema</span></em></p><p><span>Step 3: Deploy the app</span></p><p><span>Upload the folder to your workspace and deploy it. Deploying is what creates the app&#8217;s service principal, and that service principal is the identity you&#8217;ll grant data access to in the next step. So deploy first.</span></p><p><span>databricks workspace import-dir ./mcp-metric-view-search \</span></p><p><span>  /Workspace/Users/&lt;email id&gt;/mcp-metric-view-search --overwrite</span></p><p><span>databricks apps create metric-view-search-app</span></p><p><span>databricks apps deploy metric-view-search-app \</span></p><p><span>  --source-code-path /Workspace/Users/&lt;email id&gt;/mcp-metric-view-search</span></p><p><span>You can do the same thing from the Apps UI if you prefer.</span></p><p><span>Step 4: Find the app&#8217;s service principal</span></p><p><span>databricks apps get metric-view-search-app</span></p><p><span>Look at the service_principal_name field. It&#8217;ll be something like </span><em><span>xxxx-mcp-metric-view-search</span></em><span>. The app runs as this identity, so this is what needs to read your data, not your own user account.</span></p><p><span>Step 5: Grant the service principal access to your data</span></p><p><span>The app reads metric views through the warehouse, so the service principal needs three Unity Catalog grants plus warehouse access. Run each GRANT on its own. Databricks won&#8217;t accept several of them in a single statement.</span></p><p><span>GRANT USE CATALOG ON CATALOG your_catalog TO `xxxx-mcp-metric-view-search`;</span></p><p><span>GRANT USE SCHEMA ON SCHEMA your_catalog.your_schema TO `xxxx-mcp-metric-view-search`;</span></p><p><span>GRANT SELECT ON SCHEMA your_catalog.your_schema TO `xxxx-mcp-metric-view-search`;</span></p><p><span>Then give it the warehouse:</span></p><p><span>GRANT CAN USE ON WAREHOUSE `your_warehouse_id` TO `xxxx-mcp-metric-view-search`;</span></p><p><span>Repeat the schema grants for every schema you listed in config.yaml.</span></p><p><span>Step 6: Confirm it&#8217;s actually indexing</span></p><p><span>Redeploy so the app re-scans with its new permissions, then check any one of these:</span></p><p><span>- Open the app URL in a browser. The landing page shows how many metric views it has indexed.</span></p><p><span>- Hit the /health endpoint. It returns {&#8221;status&#8221;: &#8220;ok&#8221;, &#8220;indexed_metric_views&#8221;: N}.</span></p><p><span>- Read the app logs. You want to see the Indexed N metric view(s). If you instead see Cannot list catalogs or the scope still shows your_catalog_name, the config or the grants didn&#8217;t land.</span></p><p><span>Step 7: Let your teammates and their agents in</span></p><p><span>Everything above enables the app to read your data. To let other people connect their agents to it, grant them permission on the app itself. There are two ways to do this.</span></p><p><span>From the UI: open the app&#8217;s page, click Permissions in the top right, and add users, groups, or service principals. CAN USE is enough to connect and call the tools. CAN MANAGE also lets them redeploy and change settings.</span></p><p><span>Without the UI: use the permissions API. A PATCH adds people without disturbing anyone who already has access (a PUT would replace the entire list, so use PATCH here).</span></p><p><span>databricks api patch /api/2.0/permissions/apps/metric-view-search-app \</span></p><p><span>  --json &#8216;{</span></p><p><span>    &#8220;access_control_list&#8221;: [</span></p><p><span>      {&#8221;user_name&#8221;: &#8220;teammate@yourcompany.com&#8221;, &#8220;permission_level&#8221;: &#8220;CAN_USE&#8221;}</span></p><p><span>    ]</span></p><p><span>  }&#8217;</span></p><p><span>You can grant a group or another service principal the same way, and add several entries at once:</span></p><p><span>databricks api patch /api/2.0/permissions/apps/metric-view-search-app \</span></p><p><span>  --json &#8216;{</span></p><p><span>    &#8220;access_control_list&#8221;: [</span></p><p><span>      {&#8221;user_name&#8221;: &#8220;teammate@yourcompany.com&#8221;, &#8220;permission_level&#8221;: &#8220;CAN_MANAGE&#8221;},</span></p><p><span>      {&#8221;group_name&#8221;: &#8220;data-team&#8221;, &#8220;permission_level&#8221;: &#8220;CAN_USE&#8221;},</span></p><p><span>      {&#8221;service_principal_name&#8221;: &#8220;&lt;client-id&gt;&#8221;, &#8220;permission_level&#8221;: &#8220;CAN_USE&#8221;}</span></p><p><span>    ]</span></p><p><span>  }&#8217;</span></p><p><span>To confirm who has access, read the permissions back:</span></p><p><span>databricks api get /api/2.0/permissions/apps/metric-view-search-app</span></p><p><span>This is a different permission from Step 5. Step 5 controls what data the app can read. Step 7 controls who can use the app.</span></p><p><span>Step 8: Connect an agent</span></p><p><span>Register the app&#8217;s /mcp endpoint as an MCP server in your agent, give the connection a name like </span><em><span>metric-view-search</span></em><span>, and authenticate with a Databricks token.</span></p><p><span>For a connection that won&#8217;t expire after an hour, use a non-expiring Personal Access Token instead of a short-lived OAuth token. Then:</span></p><p><span>In the Claude Code CLI, that&#8217;s one command:</span></p><p><span>claude mcp add metric-view-search https://&lt;your-app-url&gt;/mcp \</span></p><p><span>  --header &#8220;Authorization: Bearer &lt;databricks-pat&gt;&#8221;</span></p><p><span>In a coded agent using the Claude Agent SDK, you configure the same three things (name, URL, and the auth header) in the mcpServers option, and add the tools to allowedTools so the agent can call them. See steps </span><a href="https://code.claude.com/docs/en/agent-sdk/mcp"><span>here</span></a><span> for wiring the app using the Claude Agent SDK:</span></p><p><span>options = ClaudeAgentOptions(</span></p><p><span>    mcp_servers={</span></p><p><span>        &#8220;metric-view-search&#8221;: {</span></p><p><span>            &#8220;type&#8221;: &#8220;http&#8221;,</span></p><p><span>            &#8220;url&#8221;: &#8220;https://&lt;your-app-url&gt;/mcp&#8221;,</span></p><p><span>            &#8220;headers&#8221;: {&#8221;Authorization&#8221;: &#8220;Bearer &lt;databricks-pat&gt;&#8221;},</span></p><p><span>        }</span></p><p><span>    },</span></p><p><span>    allowed_tools=[&#8221;mcp__metric-view-search__*&#8221;],</span></p><p><span>)</span></p><p><span>Either way, metric-view-search is just the connection name, and &lt;databricks-pat&gt; is the token. Once connected, the agent can search your metric views, read their definitions, and generate valid SQL. Other agents follow the same pattern.</span></p><p><span>&#9614; Note: a PAT is a standing personal credential that acts on your behalf until revoked. Fine for a demo; for shared or production use, prefer a service principal.</span></p><p><span>Other agents follow the same shape; they just differ in where you put the URL and the header. These steps may change based on your agent/tools.</span></p></blockquote><h2><strong><span>Demo</span></strong></h2><p style="text-align: justify;"><span>With a few metric views registered, an agent connected to the app can explore the semantic layer directly:</span></p><ul><li><p style="text-align: justify;"><span>Ask &#8220;what metric views do we have around revenue?&#8221; and the app returns the matching views with their measures and dimensions. Where definitions overlap (e.g., a fee-only revenue view and a blended-revenue view), both are surfaced with their descriptions so the agent or user can tell them apart.</span></p></li><li><p style="text-align: justify;"><span>For each match, the app returns an </span><strong><span>example query</span></strong><span> showing how to use the metric view.</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FTbo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FTbo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 424w, https://substackcdn.com/image/fetch/$s_!FTbo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 848w, https://substackcdn.com/image/fetch/$s_!FTbo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 1272w, https://substackcdn.com/image/fetch/$s_!FTbo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FTbo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png" width="1215" height="446" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:446,&quot;width&quot;:1215,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FTbo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 424w, https://substackcdn.com/image/fetch/$s_!FTbo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 848w, https://substackcdn.com/image/fetch/$s_!FTbo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 1272w, https://substackcdn.com/image/fetch/$s_!FTbo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70ba9e54-b41c-4434-ad60-6d86a55a0343_1215x446.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hHbq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hHbq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 424w, https://substackcdn.com/image/fetch/$s_!hHbq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 848w, https://substackcdn.com/image/fetch/$s_!hHbq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 1272w, https://substackcdn.com/image/fetch/$s_!hHbq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hHbq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png" width="1091" height="639" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:639,&quot;width&quot;:1091,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hHbq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 424w, https://substackcdn.com/image/fetch/$s_!hHbq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 848w, https://substackcdn.com/image/fetch/$s_!hHbq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 1272w, https://substackcdn.com/image/fetch/$s_!hHbq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68e0e3b5-4355-4fb1-8ee5-2be1fc57f4e6_1091x639.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>When do I need it?</span></strong></p><p style="text-align: justify;"><span>A few situations where searching the semantic layer directly is useful:</span></p><ul><li><p style="text-align: justify;"><span>Use a semantic layer from your own agents. You&#8217;ve standardized KPIs as metric views and want custom agents to use them without having to stand up additional consumer surfaces.</span></p></li><li><p style="text-align: justify;"><span>Partners and BI tools built on Databricks that want to explore available metrics programmatically to build more intelligent experiences.</span></p></li><li><p style="text-align: justify;"><span>Governing a large semantic layer. Data stewards find overlapping or duplicate metric views by keyword and owner across thousands of definitions, then audit and consolidate them.</span></p></li><li><p style="text-align: justify;"><span>Standardizing across a multicloud, multi-agent estate. Metric views are being open-sourced, so the same pattern can front a semantic layer wherever it runs.</span></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Best practices for GPU Model Training on Databricks AI Runtime- Part 1 ]]></title><description><![CDATA[Practical guide for MLEs who are training models on GPUs]]></description><link>https://www.databricksters.com/p/best-practices-for-gpu-model-training</link><guid isPermaLink="false">https://www.databricksters.com/p/best-practices-for-gpu-model-training</guid><dc:creator><![CDATA[Gautam Srinivasan]]></dc:creator><pubDate>Fri, 24 Jul 2026 15:01:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!6ruA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span><br></span><strong><span>TL;DR</span></strong></p><ul><li><p><span>Choose the right entry point: use notebooks with the </span><code>@distributed</code><span> decorator for rapid prototyping, and switch to the AIR CLI with YAML-based configs for long-running, multi-node production training.</span></p></li><li><p><span>Pick the right accelerator for the job: attach a low-cost A10 or single H100 for orchestration, and reserve multi-GPU or multi-node H100 configurations for actual training workloads.</span></p></li><li><p><span>Use </span><code>UCVolumeDataset</code><span> and </span><code>serverless_gpu.data.DataLoader</code><span> for efficient data loading with local caching, automatic distributed partitioning, and optimized prefetching keep GPUs fed without Spark overhead.</span></p></li><li><p><span>Enable MLflow autologging and monitor the System Metrics tab for GPU utilization and memory; set a custom run name and </span><code>MLFLOW_RUN_ID</code><span> to resume previous training runs.</span></p></li></ul><div><hr></div><p><span>This post covers the practical lessons we&#8217;ve learned from working with teams in Databricks and with customers shipping real GPU training workloads. Think of it as the &#8220;</span><em><span>things we wish someone had told us on day one</span></em><span>&#8221; guide.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><span>If you&#8217;re training or fine-tuning deep learning models on Databricks, AI Runtime is how you get access to GPUs without the usual infrastructure headaches. But like any powerful tool, knowing how to use it well makes all the difference.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6ruA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6ruA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 424w, https://substackcdn.com/image/fetch/$s_!6ruA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 848w, https://substackcdn.com/image/fetch/$s_!6ruA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 1272w, https://substackcdn.com/image/fetch/$s_!6ruA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6ruA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png" width="1456" height="694" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:694,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:201547,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/207084009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6ruA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 424w, https://substackcdn.com/image/fetch/$s_!6ruA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 848w, https://substackcdn.com/image/fetch/$s_!6ruA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 1272w, https://substackcdn.com/image/fetch/$s_!6ruA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F655989a8-8356-4650-9964-80da3cf6cff4_1861x887.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>What is AI Runtime? How Can It Help?</span></strong></p><p><span>AI Runtime is a serverless model training product at Databricks built specifically for deep learning workloads. It brings GPU support to Databricks Serverless, so you can train and fine-tune models using your favorite frameworks (PyTorch, DeepSpeed, vLLM, Ray, etc.) without managing any infrastructure.</span></p><p><span>Before AI Runtime, running GPU workloads meant configuring clusters, picking the right drivers, managing autoscaling, and often negotiating GPU capacity directly with your cloud provider (AWS, Azure, or GCP). AI Runtime takes all of that off your plate.</span></p><p><strong><span>Key benefits:</span></strong></p><ul><li><p><strong><span>Fully managed GPU infrastructure</span></strong><span>: You request the GPUs you need and start working. Pay only for what you use. On-demand by nature.</span></p></li><li><p><strong><span>Seamless scaling</span></strong><span>. Go from a single GPU experiment to multi-node distributed training without reconfiguring anything.</span></p></li><li><p><strong><span>A runtime dedicated for deep learning</span></strong><span>: Choose either a minimal base environment for full control over your dependencies, or a full-featured AI environment pre-loaded with popular ML frameworks. Either way, CUDA, NCCL, and drivers are already set up. For multi-node H100s, RDMA networking is configured out of the box.</span></p></li><li><p><strong><span>Natively integrated</span></strong><span> across notebooks, jobs, Unity Catalog, and MLflow. Your development workflow, data access, experiment tracking, and governance all work together.</span></p></li></ul><div><hr></div><p><span>Here are the 5 AI Runtime best practices we will cover in this Part 1 blog. Feel free to jump directly to the section that&#8217;s interesting for you:</span></p><ul><li><p><span>What are my hardware accelerator options?</span></p></li><li><p><span>What are environments in AI Runtime?</span></p></li><li><p><span>What are the 3 ways of using AI Runtime- notebooks, CLI and IDE?</span></p></li><li><p><span>How can I use MLflow for tracing and observability?</span></p></li><li><p><span>What are the different options for data loading for training?</span></p></li></ul><p><strong><span>1. Hardware Accelerator Options</span></strong></p><p><span>The following </span><a href="https://docs.databricks.com/aws/en/machine-learning/ai-runtime/#hardware-options"><span>accelerators</span></a><span> are currently supported. All accelerators provision a single node.</span></p><ul><li><p><code>1xA10</code><span>- 24 GB-Small to medium ML and deep learning tasks</span></p></li><li><p><code>1xH100</code><span>- 80 GB- Workloads that need more GPU memory than </span><code>1xA10</code><span> provides but do not require multi-GPU distributed training.</span></p></li><li><p><code>8xH100</code><span>- 80 GB per GPU- Large-scale AI workloads that need distributed training or multiple GPUs</span></p></li></ul><p><span>For multi-GPU workloads, choose a single-node </span><code>8xH100</code><span> instance (one node with 8 H100 GPUs). Partial multi-GPU configurations like </span><code>2xH100</code><span> or </span><code>4xH100</code><span> are not supported though. </span></p><p><span>For multi-node workloads, scale out using multiple </span><code>8xH100</code><span> nodes; for example, 4 nodes with 8 H100 GPUs each give you a total of 32 H100 GPUs. Or you can use multiple nodes of </span><code>1xA10</code><span>.</span></p><p><span>Besides the options listed above, be sure to consult the latest documentation, as additional accelerator types may become available over time.</span></p><p><strong><span>2. Environments</span></strong></p><p><span>AI Runtime offers two managed Python environments, the default base environment and the Databricks AI environment.</span></p><p><em><span>Default base environment</span></em></p><ul><li><p><span>Minimal, includes only required packages: torch, cuda, and torchvision</span></p></li><li><p><span>You want full control over your dependency stack and prefer to install only what you need</span></p></li></ul><p><em><span>Databricks AI environment</span></em></p><ul><li><p><span>Pre-loaded with popular ML frameworks (PyTorch, Transformers, and more)</span></p></li><li><p><span>You want a complete environment for training, fine-tuning, and experimentation without manual dependency management</span></p></li></ul><p><span>You can also use a </span><a href="https://docs.databricks.com/aws/en/compute/serverless/dependencies#base-environment"><span>workspace base environment</span></a><span> that a workspace admin has built for serverless GPU compute. <br><br></span><strong><span>Environment Caching</span></strong><span>: Environments are cached across sessions to speed up startup times. When you reconnect to AI Runtime with the same environment configuration, previously installed packages may be available from cache, reducing setup time. However, this behavior is not guaranteed, so always ensure your notebook includes the necessary %pip install commands for reproducibility..</span></p><p><strong><span>Import custom modules</span></strong><span>: Import custom modules by placing them under /Workspace/shared and adding the path to sys.path. Or You can also upload module files as Workspace files and import them directly.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;68bf4446-d01a-4627-ac51-cdd80c1cfde8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">import sys

sys.path.append(&#8221;/Workspace/Shared/my-project/src&#8221;)

from my_module import my_function</code></pre></div><p><span>Some differences as compared to Classic Compute:</span></p><ul><li><p><strong><span>Spark</span></strong><span>: AI Runtime is a Python-only environment; Spark is not available as a local runtime. So you cannot use PySpark functions but you can use Spark Connect for data loading.</span></p></li><li><p><strong><span>MLR</span></strong><span>: Pre-installed AI Runtime packages do not fully replace Databricks Runtime ML, because some ML libraries available in Databricks Runtime ML are not included.</span></p></li></ul><p><strong><span>3. Main Ways to Get Started: Notebooks, CLI and IDE</span></strong></p><p><span>You have two primary ways for running workloads on AI Runtime: </span><strong><span>Notebooks</span></strong><span> and </span><strong><span>AIR CLI</span></strong><span>.  Each has its strengths, and picking the right one for your use case makes the journey easier.</span></p><p><strong><span>3a. Notebooks</span></strong></p><p><span>Notebooks on AI Runtime let you develop interactively, iterate quickly, and visualize results in place. Simply choose your preferred accelerator (A10, 1&#215;H100, or 8&#215;H100), attach it to your notebook, and your code will execute directly on that GPU-backed node.</span></p><p><em><span>Multi-GPU (Single Node)</span></em></p><p><span>To run jobs across multiple GPUs on a single node, use the </span><code>@distributed</code><span> decorator. Wrap your training workflow inside a function and apply the decorator to it. This function becomes the entry point for distributed execution, so all the components - model initialization, data loading, and training logic - should be defined within it.<br>The </span><code>@distributed</code><span>  API integrates with major distributed training libraries: PyTorch, DDP, FSDP and DeepSpeed</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;6ac6784a-b8e3-4357-8fdc-8ef4d5e9b91b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">@distributed(gpus=8, gpu_type=&#8217;H100&#8217;)

def run_train(num_epochs, batch_size):

    # training code here


run_train.distributed(num_epochs=3, batch_size=1)
</code></pre></div><p><em><span>Multi-Node</span></em></p><p><span>Notebooks also support remote execution which lets you submit work to a different set of remote GPUs than your attached compute. To enable this, set </span><code>remote=True</code><span> in the </span><code>@distributed</code><span> decorator. This makes it easy to scale beyond a single node without interrupting your workflow.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;dd622bc3-7227-45a2-83c5-ee42e2f26d4b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">@distributed(gpus=32, gpu_type=&#8217;H100&#8217;, remote=True)

def run_train(num_epochs, batch_size):

    # training code here</code></pre></div><p><em><span>Ray</span></em></p><p><span>For a single node (A10 or 8xH100), you can just use </span><code>ray.init()</code><span>.</span></p><p><span>For multi-node, use </span><code>@ray_launch</code><span>  decorator</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;2ead0e5e-fee5-40bc-a2a2-fe9e553f6618&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from serverless_gpu.ray import ray_launch


@ray_launch(gpus=16, gpu_type=&#8217;h100&#8217;)

def run_ppo(config) -&gt; None:

&#9;# your ray training code here


run_ppo.distributed(DictConfig(config))</code></pre></div><p><span>Best practices:</span></p><ul><li><p><span>Pin your library versions: AI Runtime has regular releases. If you need reproducibility, pin the versions that work for your code.</span></p></li><li><p><span>For distributed training, any environment variable needed by your training code should be set within </span><code>@distributed</code><span>. Note: Some environment variables must be set before importing a particular library so that it initializes with the correct configuration and hardware settings. This is specific to the library, not AI runtime.</span></p></li></ul><ul><li><p><span>For remote multi-GPU H100 jobs, it&#8217;s more efficient to attach your notebook to a lower-cost accelerator such as an A10 or a single H100. Since the actual training runs on remote GPU nodes, your local notebook compute is only used for orchestration.</span></p></li><li><p><span>Keep experimentation in notebooks, production in jobs: Notebooks are great for prototyping and debugging, but once your code is stable, you can move it to a scheduled job. For scheduling notebook jobs, use the Jobs API or Declarative Automation Bundles (fka Databricks Asset Bundles)</span></p></li><li><p><span>For operations that do not require GPUs (for example, converting data formats, or exploratory data analysis), attach your notebook to a CPU cluster to preserve GPU resources.</span></p></li><li><p><span>Note: Connection to your interactive compute auto-terminates after 60 minutes of inactivity.</span></p></li></ul><p><strong><span>3b. AI Runtime CLI</span></strong></p><p><code>air</code><span> CLI is a command-line tool for submitting and managing training jobs on Databricks Serverless GPU compute directly from your terminal, instead of using notebooks.The CLI uses YAML-based job configuration, and supports workspace-based and git-based code workflows. It&#8217;s aimed for users who prefer repos, IDEs, git, and CI/CD-style workflows over notebooks.</span></p><p><span>SGCLI follows a simple </span><em><span>submit &#8594; schedule &#8594; execute &#8594; observe</span></em><span> loop:</span></p><ol><li><p><strong><span>Define</span></strong><span> your workload in a </span><code>train.yaml</code><span> file (compute shape, environment, launch command).</span></p></li><li><p><strong><span>Submit</span></strong><span> via </span><code>sgcli run -f workload.yaml</code></p></li><li><p><strong><span>AIR automatically provisions</span></strong><span> the requested GPU nodes, installs dependencies from requirements.yaml, and injects coordination environment variables</span></p></li><li><p><strong><span>Your training code runs</span></strong><span> (e.g., via torchrun) across the provisioned nodes.</span></p></li><li><p><strong><span>Observe</span></strong><span> progress through  </span><code>sgcli get logs</code><span>,  </span><code>sgcli get status</code><span>, or the MLflow UI for GPU metrics.</span></p></li></ol><p><span>Here is a simple </span><code>train.yaml</code></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;4e6611b8-e989-416b-9aee-159ea3e6d6a2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">experiment_name: simple-training

environment:

  dependencies: requirements.yaml

env_variables:

  NCCL_DEBUG: &#8220;INFO&#8221;

secrets:

  HF_TOKEN: &#8216;my_scope/hf_token&#8217;

compute:

  num_accelerators: 8

  accelerator_type: GPU_8xH100

code_source:

  type: snapshot

    snapshot:

      root_path: /home/username/repo

      git:

        branch: main

command: torchrun --nproc_per_node=8 train.py

max_retries: 2

timeout_minutes: 90

usage_policy_id: abcd123-25b8-3e87-9a2c-f86eb19d101c</code></pre></div><p>Here is the <a href="https://docs.databricks.com/aws/en/machine-learning/ai-runtime/cli/">doc</a> for AI CLI for more information.</p><p><strong><span>Best practices for AIR CLI:</span></strong></p><ul><li><p><span>Use CLI for multi-node, long-duration jobs: If your training run takes hours or days and spans multiple nodes, AIR CLI is the right tool. It handles the orchestration cleanly.</span></p></li><li><p><span>Treat the workload.yaml as the source of truth. Keep the launch definition in version-controlled YAML rather than relying on notebook/UI state, so runs are easier to reproduce, hand off, and compare later.</span></p></li><li><p><span>Check </span><code>workload.yaml</code><span> and requirements.yaml into your repo along with </span><code>train.py</code><span> for version tracking and reproducibility.</span></p></li></ul><p><a href="https://docs.databricks.com/aws/en/machine-learning/ai-runtime/cli/docker-images"><span>Custom Docker Images</span></a><span>: You can run your own Docker images with AI Runtime using a simple CLI and YAML-based configuration. This is useful for workloads that need specific libraries, complex dependencies, fully reproducible environments, or standard images defined by your organization.</span></p><p><strong><span>When to use AIR CLI vs. Notebooks:</span></strong></p><p><span>Use notebooks (with the </span><code>@distributed</code><span> decorator) for rapid iteration and prototyping.</span></p><p><span>Use the AI Runtime CLI when you need to:</span></p><ul><li><p><span>Submit GPU training jobs directly from your terminal.</span></p></li><li><p><span>Define jobs in YAML for version control and reproducibility.</span></p></li><li><p><span>Run long (multi-day) or distributed (multi-node) training that shouldn&#8217;t depend on an interactive session.</span></p></li></ul><p><strong><span>3c. Connecting from the IDE terminal like Visual Studio or Cursor</span></strong></p><p><span>You can connect to AI Runtime on serverless GPU compute directly from a terminal in your IDE through an SSH tunnel.</span></p><p><span>To connect from a terminal within your IDE, run the databricks ssh connect command with the --accelerator option.</span></p><p><code>databricks ssh connect --accelerator=GPU_1xA10</code></p><p><span>To connect and start the session in Visual Studio Code or Cursor, use the </span><code>--ide</code><span> option. The CLI opens an IDE window pointing to the home workspace folder.</span></p><p><code>databricks ssh connect --ide=vscode</code></p><p><strong><span>4. Use MLflow for Tracking and Observability</span></strong></p><p><span>On AI Runtime, MLflow is the main place to track and inspect training runs. AIR is natively integrated with MLflow for experiment tracking, model logging, metric visualization, and observability.</span></p><p><span>It&#8217;s recommended to use the latest version of </span><code>mlflow</code><span> (minimum 3.7 version)</span></p><p><span>For each run of your experiment:</span></p><ul><li><p><span>The </span><strong><span>System Metrics</span></strong><span> tab provides an overview of GPU, CPU, and disk performance, including key metrics such as utilization and memory usage. These are generated automatically for each run. Use these metrics to proactively identify bottlenecks, tune workloads (for example by adjusting batch size or data loading), and ensure resources are right-sized so you maintain high utilization without over-provisioning</span></p></li><li><p><span>The </span><strong><span>Model Metrics</span></strong><span> tab surfaces key performance indicators logged during training, such as accuracy, loss, and other task-specific scores. Use these metrics to validate and compare experiments, ensuring that changes to model architecture, training configuration (for example, batch size, learning rate, and data preprocessing),etc actually improve model quality over time/</span></p></li><li><p><strong><span>Logs</span></strong><span> for processes running on each GPU are stored as artifacts and can be accessed from the MLflow UI. For notebook-based runs, the same logs are also visible directly in the notebook cell outputs. Use these logs to monitor training progress, debug training issues, and trace performance regressions across runs so you can quickly identify and fix problems in your training pipeline.</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2eTK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2eTK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 424w, https://substackcdn.com/image/fetch/$s_!2eTK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 848w, https://substackcdn.com/image/fetch/$s_!2eTK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 1272w, https://substackcdn.com/image/fetch/$s_!2eTK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2eTK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png" width="1456" height="1070" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1070,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2eTK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 424w, https://substackcdn.com/image/fetch/$s_!2eTK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 848w, https://substackcdn.com/image/fetch/$s_!2eTK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 1272w, https://substackcdn.com/image/fetch/$s_!2eTK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbffab97a-e6fa-49ae-9f26-6363b63b5f0a_2048x1505.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><span>System Metrics</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bgx7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bgx7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 424w, https://substackcdn.com/image/fetch/$s_!bgx7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 848w, https://substackcdn.com/image/fetch/$s_!bgx7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 1272w, https://substackcdn.com/image/fetch/$s_!bgx7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bgx7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png" width="1456" height="842" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:842,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bgx7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 424w, https://substackcdn.com/image/fetch/$s_!bgx7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 848w, https://substackcdn.com/image/fetch/$s_!bgx7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 1272w, https://substackcdn.com/image/fetch/$s_!bgx7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5327686b-556b-4148-ad9b-24e88fd929f0_2048x1185.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><span>Logs are also available as artifacts in the MLflow run</span></p><p><strong><span>Best practices:</span></strong></p><ul><li><p><span>Autologging: MLflow supports automatic logging for popular frameworks like PyTorch, TensorFlow etc. You can enable it for your training job by adding just one of code,  </span><code>mlflow.autolog()</code><span>. It automatically captures:</span></p><ul><li><p><span>Metrics: standard training and evaluation measures (such as accuracy and F1 score);</span></p></li><li><p><span>Parameters: hyper-parameters, such as learning rate and number of estimators</span></p></li><li><p><span>Artifacts: output files, such as your trained model weights.</span></p></li></ul></li><li><p><span>Manual Logging: you can manually log any parameter, metric or artifact in your training code.</span></p></li><li><p><span>The Serverless GPU API automatically launches an MLflow experiment with default name /Users/{WORKSPACE_USER}/{get_notebook_name()}. To overwrite it, set</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;7dda7fed-8376-4948-ae12-fd486f924966&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import os

os.environ[&#8221;MLFLOW_EXPERIMENT_NAME&#8221;] = &#8220;/Users/&lt;username&gt;/my-experiment&#8221;</code></pre></div><ul><li><p><span>It&#8217;s recommended to customize the MLflow run name. It enables you to restart from a previous run.You can customize the run name using the run_name parameter in  </span><code>mlflow.start_run(run_name=&#8221;your-custom-name)</code><span> or in third-party libraries that support MLflow (for example, Hugging Face Transformers). Otherwise, the default run name is </span><code>jobTaskRun-xxxxx</code><span>.</span></p></li><li><p><span>Resume previous training by setting the MLFLOW_RUN_ID from the earlier run: </span><code>mlflow.start_run(run_id=&#8221;&lt;previous-run-id&gt;&#8221;)</code></p></li><li><p><span>Set the `step` parameter in </span><em><span>MLFlowLogger</span></em><span> to reasonable batch numbers. MLflow has a limit of 10 million metric steps, so logging every single batch on large training runs can hit this limit</span></p></li></ul><p><strong><span>Monitor GPU resources</span></strong><span>:</span></p><p><span>If you are using Notebook, you can use </span><strong><span>GPU resources</span></strong><span> in the right side pane to monitor GPU health and utilization while your code runs on AI Runtime. The pane supports both single-node and multi-node workloads. The pane displays the following metrics for each GPU: GPU utilization percentage, GPU memory usage, Temperature.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yF6p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yF6p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 424w, https://substackcdn.com/image/fetch/$s_!yF6p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 848w, https://substackcdn.com/image/fetch/$s_!yF6p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 1272w, https://substackcdn.com/image/fetch/$s_!yF6p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yF6p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png" width="1388" height="1829" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1829,&quot;width&quot;:1388,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yF6p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 424w, https://substackcdn.com/image/fetch/$s_!yF6p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 848w, https://substackcdn.com/image/fetch/$s_!yF6p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 1272w, https://substackcdn.com/image/fetch/$s_!yF6p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc82c126e-b0a1-4dc9-8531-434aa628f871_1388x1829.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;">GPU metrics</p><p><span>If you are using CLI, you can utilize System Metrics to view GPU metrics.</span></p><p><strong><span>5. Data Loading</span></strong></p><p><strong><span>Tabular Data:</span></strong></p><ul><li><p><span>Small Dataset: For small Delta tables, use Spark Connect to load the data. Then convert the Spark DataFrame to a pandas DataFrame with </span><em><span>toPandas()</span></em><span>. If needed, you can further convert it to a NumPy array using </span><em><span>to_numpy()</span></em><span>. This works well for Single Node</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;36663fa1-dbd7-465d-bc8d-e0d29f4f7193&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Load Delta table

df = spark.read.format(&#8221;delta&#8221;).table(&#8221;your_catalog.your_schema.your_table&#8221;)

# Convert Spark DataFrame to pandas

pdf = df.toPandas()

# Optional: convert pandas DataFrame to NumPy array

arr = pdf.to_numpy()</code></pre></div><ul><li><p><span>Large dataset: For large Delta tables that are too big to convert with </span><em><span>toPandas()</span></em><span>, export the data to a Unity Catalog volume and load it directly using PyTorch or Hugging Face. This approach avoids Spark overhead during training and works well for both single-GPU and distributed training workflows.</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;1ce539ae-0e98-4dc6-b262-cc97a811d12f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Step 1: Export the Delta table to Parquet files in a UC volume

output_path = &#8220;/Volumes/catalog/schema/my_volume/training_data&#8221;

spark.table(&#8221;catalog.schema.my_table&#8221;).write.mode(&#8221;overwrite&#8221;).parquet(output_path)

# Step 2: Load the exported data directly using Hugging Face datasets

from datasets import load_dataset

dataset = load_dataset(&#8221;parquet&#8221;, data_files=&#8221;/Volumes/catalog/schema/my_volume/training_data/*.parquet&#8221;)</code></pre></div><p><strong><span>Unstructured Data</span></strong><span>:</span></p><p><span>For unstructured data such as images, audio, and text files stored in Unity Catalog volumes, our recommendation is to use </span><em><span>UCVolumeDataset</span></em><span> from the </span><em><span>serverless_gpu.data</span></em><span> package. </span></p><p><em><span>UCVolumeDataset</span></em><span> is a PyTorch </span><em><span>IterableDataset</span></em><span> that copies each file from the volume to a fast local cache on first access and yields the cached local file path. Besides local caching it supports automatic partitioning of files and divided across DataLoader workers, so each (rank, worker) pair receives a non-overlapping slice.</span></p><p><span>For optimal performance, pair </span><em><span>UCVolumeDataset</span></em><span> with </span><em><span>serverless_gpu.data.DataLoader</span></em><span>, rather than the stock PyTorch </span><em><span>DataLoader</span></em><span>, as it is tuned for serverless GPU I/O and fetches and caches files concurrently while the GPU computes.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;0c006d7c-cb78-4cd7-98a3-caa01cdfe0fd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from serverless_gpu.data import UCVolumeDataset

from torch.utils.data import IterableDataset

from PIL import Image

import torchvision.transforms.functional as TF

class ImageDataset(IterableDataset):

    &#8220;&#8221;&#8220;Decodes each cached file path from UCVolumeDataset into a tensor.&#8221;&#8220;&#8221;

    def __init__(self, path_dataset: UCVolumeDataset):

        self._path_dataset = path_dataset

    def __iter__(self):

        for local_path in self._path_dataset:

            image = Image.open(local_path).convert(&#8221;RGB&#8221;)

            yield TF.to_tensor(image)

path_dataset = UCVolumeDataset(&#8221;/Volumes/catalog/schema/my_volume/images&#8221;)

dataset = ImageDataset(path_dataset)</code></pre></div><p><span>For more info about data loading, here is the </span><a href="https://docs.databricks.com/aws/en/machine-learning/ai-runtime/dataloading"><span>doc</span></a><span>.</span></p><p><strong><span>Best Practices:</span></strong></p><ul><li><p><span>Load data inside the </span><em><span>@distributed</span></em><span> decorator: For distributed training, move data loading code inside the @distributed decorator. The dataset size can exceed the maximum size allowed by pickle, so it is recommended to generate the dataset inside the decorator.</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;a9317af1-8f44-4f86-ab5a-17ec9d425a0d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from serverless_gpu import distributed

# This may cause a pickle error if the dataset is too large

dataset = get_dataset(file_path)

@distributed(gpus=8, gpu_type=&#8217;H100&#8217;)

def run_train():

   # Load data inside the decorator to avoid pickle serialization issues

   dataset = get_dataset(file_path)

   ...</code></pre></div><p><span>When you build a </span><em><span>UCVolumeDataset</span></em><span> in the decorator, it uses torch.distributed rank info at runtime to automatically split files across ranks, so you don&#8217;t need a DistributedSampler for file-based volume data.</span></p><ul><li><p><span>Use </span><em><span>UCVolumeDataset</span></em><span> for multi-epoch training so files are cached locally on first access and reused, instead of copying the entire volume upfront with shutil.copytree when you may only read part of it.</span></p></li></ul><ul><li><p><span>Cache data locally for multi-epoch training. For large datasets, copy datasets to /tmp for faster access across epochs:</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;09c5c85b-9976-4732-a0e3-99a5cbbb0194&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import shutil
shutil.copytree(&#8221;/Volumes/catalog/schema/volume/dataset&#8221;, &#8220;/tmp/dataset&#8221;)</code></pre></div><ul><li><p><span>Parallelize data fetching: Use </span><em><span>serverless_gpu.data.DataLoader</span></em><span> to parallelize fetching. It&#8217;s a torch DataLoader subclass optimized for serverless GPUs, with higher defaults for worker count and prefetching so files are loaded and cached while the GPU runs. It also records per-batch load times in MLflow to help identify data bottlenecks.</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;0493d348-b04c-4c5c-b627-38558fdde86e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from serverless_gpu.data import DataLoader

loader = DataLoader(

    dataset,

    batch_size=32,

    pin_memory=True,

    # num_workers=6, by default

    # prefetch_factor=4, by default

    # raise num_workers to increase parallel reads, or prefetch_factor to deepen each worker&#8217;s queue.

)</code></pre></div><ul><li><p><span>Adjust batch size: For large datasets, use a larger batch size so the cost of loading data per batch is spread over more samples, reducing file fetches each step. If GPU memory is tight, pair a larger batch with gradient accumulation to keep the same effective batch size.</span></p></li></ul><ul><li><p><span>Use Streaming datasets: For very large datasets that do not fit in memory, use streaming approaches.</span></p><ul><li><p><em><span>UCVolumeDataset</span></em><span> from </span><em><span>serverless_gpu.data</span></em><span> for streaming files from Unity Catalog volumes with local caching and automatic distributed partitioning.</span></p></li><li><p><span>PyTorch </span><em><span>IterableDataset</span></em><span> for custom streaming logic.</span></p></li><li><p><span>Hugging Face datasets with streaming for datasets hosted on the Hub or in volumes.</span></p></li><li><p><span>RayData for distributed batch data processing</span></p></li></ul></li></ul><div><hr></div><h2><strong><span>FAQs</span></strong></h2><p><strong><span>Q: My training run is slow and GPU utilization is low. What should I check first?</span></strong></p><p><span>A: Start by inspecting the </span><strong><span>System Metrics</span></strong><span> tab in MLflow, </span>focusing on GPU utilization, GPU memory usage, and data loading throughput. If GPU utilization is low while the GPU is not memory-bound, your data pipeline is likely the bottleneck. <span>We recommend increasing your batch size or switching to </span><code>serverless_gpu.data.DataLoade</code><span>r, which is specifically tuned for serverless I/O. Additionally, ensure you are using </span><code>UCVolumeDataset</code><span> to cache files locally instead of refetching them from Unity Catalog during every step.</span></p><p><strong><span>Q: Should I use notebooks or the AIR CLI for my training workload?</span></strong></p><p><span>A: It depends on your workflow phase. Use notebooks with the </span><em><span>@distributed</span></em><span> decorator for rapid prototyping and interactive debugging. Switch to the AIR CLI when you need production-grade features like version-controlled YAML configurations, multi-day training runs, or a CI/CD-style approach directly from your IDE.</span></p><p><strong><span>Q: How do I resume a previous MLflow training run?</span></strong><span> <br></span><strong><span>A:</span></strong><span> Set a custom run name using </span><em><span>mlflow.start_run(run_name=&#8221;your-custom-name&#8221;)</span></em><span> so you can identify the run later. To resume, call </span><em><span>mlflow.start_run(run_id=&#8221;&lt;previous-run-id&gt;&#8221;) </span></em><span>with the earlier run&#8217;s ID. This lets you continue logging metrics and artifacts into the same run record.</span></p><p><strong><span>Q: Which environment should I choose &#8212; the base environment or the AI environment? </span></strong><span><br>A: Start with the AI environment (recommended) as it has popular ML frameworks (PyTorch, DeepSpeed, vLLM, Ray, etc.) pre-installed. </span>Use the base environment if you need full control over your dependencies and want a minimal setup. You can also use a base environment defined by your workspace admin, built on organization-standard images<span>. Always pin library versions for reproducibility.</span></p><div><hr></div><p><span>Thanks for reading. Feel free to share any feedback on topics you&#8217;d like to see next in the comments section.</span></p><p><span>Shout out to the AI Runtime PM, </span><a href="https://www.linkedin.com/in/tejassundaresan/"><span>Tejas Sundaresan</span></a><span> for helping us review the content in this blog.</span></p><p><span>We plan to work on Part 2 for this AI Runtime blog series next. In this post, we will share some more best practices around remote development options, cost and usage monitoring, and using genie code with AI Runtime. Stay tuned!</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/p/best-practices-for-gpu-model-training/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.databricksters.com/p/best-practices-for-gpu-model-training/comments"><span>Leave a comment</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[The Spark Best Practices You Learned Are Already Out of Date]]></title><description><![CDATA[Why traditional tuning wisdom is slowing down your pipelines, and how to optimize high-throughput for modern NVMe and silicon]]></description><link>https://www.databricksters.com/p/the-spark-best-practices-you-learned</link><guid isPermaLink="false">https://www.databricksters.com/p/the-spark-best-practices-you-learned</guid><dc:creator><![CDATA[Canadian Data Guy]]></dc:creator><pubDate>Tue, 14 Jul 2026 15:02:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!M29Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My colleague and I spent the past few weeks hyper-tuning a Spark streaming workload &#8212; millions of rows per second, at the lowest cost we could manage, without breaking our latency SLA. What we found didn&#8217;t match what either of us expected going in.</p><p>Here are five lessons we learned over a week of benchmarking on Spark 4</p><h2>Rule 1: Upgrade the Silicon (M8/M9, Not M5/M6)</h2><p>Before tuning shuffle partitions or threading, pick the right generation.</p><p>On the same workload &#8212; same Databricks Runtime, same node count, same pipeline &#8212; we moved from M6gd to M8gd and saw a rare three-way win:</p><ul><li><p>2&#215; throughput (more records per second)</p></li><li><p>~38% lower cost per record processed</p></li><li><p>Lower batch latency end to end</p></li></ul><p>Newer Graviton generations (M8, and M9 when available) bring better CPU, memory bandwidth, and local NVMe behavior. That matters most when every micro-batch does shuffle, spill, and multi-table writes.</p><p><strong>Practice:</strong> Don&#8217;t lock onto M5/M6 families because they&#8217;re familiar. Benchmark newest generation up on the same job before you optimize code. Often the biggest lever is hardware, not another <code>spark.conf</code> tweak.</p><h2>Rule 2: Multi-Thread Your foreachBatch Sinks</h2><p><code>foreachBatch</code> runs once per micro-batch. If you write to more than one table (main, audit, dead-letter), sequential writes leave the cluster idle between commits.</p><p><strong>Practice:</strong> Do transforms once, split the batch, submit writes in parallel from the driver.</p><pre><code><code>from concurrent.futures import ThreadPoolExecutor

def process_batch(batch_df, batch_id):
    with ThreadPoolExecutor(max_workers=2) as executor:
        futures = [
            executor.submit(write_to_table_a, batch_df, batch_id),
            executor.submit(write_to_table_b, batch_df, batch_id),
        ]
        for f in futures:
            f.result()</code></code></pre><p>Parallel writes mean more Spark jobs per batch &#8212; you&#8217;re trading wall-clock time for driver coordination. Profile before raising <code>max_workers</code>.</p><p>For a fuller production version of this &#8212; caching the batch once, fanning out to N tables instead of 2, propagating thread exceptions properly &#8212; see <a href="https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/parallelize_for_each_batch.py">the reference notebook</a>. Two companion videos walk through it: <a href="https://www.youtube.com/watch?v=gur1oLe5t2o">foreachBatch fundamentals</a> first, then <a href="https://www.youtube.com/watch?v=n9jodzYq1e4">parallelizing the writes</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M29Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M29Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 424w, https://substackcdn.com/image/fetch/$s_!M29Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 848w, https://substackcdn.com/image/fetch/$s_!M29Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 1272w, https://substackcdn.com/image/fetch/$s_!M29Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M29Y!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png" width="1200" height="799.4505494505495" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png&quot;,&quot;srcNoWatermark&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bff96e35-23b8-461c-942f-9e4d28055969_2528x1684.jpeg&quot;,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:970,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2073399,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/204337804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbff96e35-23b8-461c-942f-9e4d28055969_2528x1684.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M29Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 424w, https://substackcdn.com/image/fetch/$s_!M29Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 848w, https://substackcdn.com/image/fetch/$s_!M29Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 1272w, https://substackcdn.com/image/fetch/$s_!M29Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f8bcfab-ca69-4db4-ad8f-4b331cb7c218_2528x1684.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Rule 3: Spill to Local Disk, Don&#8217;t Recache What&#8217;s Already There</h2><p>Every task writes its output to the disk on the machine it runs on. You can let that spill hit the network over EBS, or you can pick an instance family with disk physically attached to the node. The attached disk wins on performance, every time.</p><p>Default is simple: don't cache. When the fork happens after a shuffle &#8212; the batch already passed through a join, aggregation, or stateful dedup &#8212; the shuffle write already left a durable copy on local disk that Spark reuses automatically for any second write path, retry or independent action alike (look for a "skipped" stage in the Spark UI), so caching here only duplicates what's already there.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!K66N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!K66N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 424w, https://substackcdn.com/image/fetch/$s_!K66N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 848w, https://substackcdn.com/image/fetch/$s_!K66N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 1272w, https://substackcdn.com/image/fetch/$s_!K66N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!K66N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png" width="1456" height="495" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:495,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50378,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/204337804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!K66N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 424w, https://substackcdn.com/image/fetch/$s_!K66N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 848w, https://substackcdn.com/image/fetch/$s_!K66N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 1272w, https://substackcdn.com/image/fetch/$s_!K66N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fceeb4593-abb4-4da0-9f0a-ff4e1dfb4687_1472x500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The judgment call lives in the other case: a fork before any shuffle, where a shared read and a few light transforms split straight into two writes with no durable file to fall back on. Skip the cache and both paths redo that shared work from scratch, source read included; cache it and you pay a serialize-then-deserialize round trip through the block manager instead. Which one wins depends on the weight of the shared transforms &#8212; a wide UDF or an expensive parse earns the cache, a couple of column selects don&#8217;t, and recomputing them twice is cheaper than the round trip.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CHHq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CHHq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 424w, https://substackcdn.com/image/fetch/$s_!CHHq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 848w, https://substackcdn.com/image/fetch/$s_!CHHq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 1272w, https://substackcdn.com/image/fetch/$s_!CHHq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CHHq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png" width="1456" height="506" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:506,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44649,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/204337804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CHHq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 424w, https://substackcdn.com/image/fetch/$s_!CHHq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 848w, https://substackcdn.com/image/fetch/$s_!CHHq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 1272w, https://substackcdn.com/image/fetch/$s_!CHHq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90060a68-f6bc-4cbc-aa52-8721959cc958_1472x512.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s the number that makes this rule worth enforcing: memory-optimized (R-series) instances carry at least a <strong>~20% price premium over general-purpose compute &#8212; on every major cloud, not just one</strong>. Cut the unnecessary caching and you cut the memory pressure that pushes you onto R-series in the first place. That 20% doesn&#8217;t vanish &#8212; you either bank it as savings or spend it on more CPU. Cache when profiling proves it pays off, not by habit.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Dozens of hours, endless caffeine, and millions of rows processed&#8212;all distilled into a quick read. Subscribe to support the grind. No paywalls, no spam, just pure takeaways.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Rule 4: Optimize for Finishing on Time, Not Just Finishing</h2><p>In a latency-bound streaming job, &#8220;did the job succeed&#8221; is the wrong question. The right one i<em>s &#8220;did it finish inside the SLA.&#8221;</em> A batch that completes in 90 seconds against a 60-second budget hasn&#8217;t crashed &#8212; it&#8217;s succeeded and missed the point.</p><p>That reframes how you pick a join strategy. Sort-merge joins are stable: they spill to disk gracefully and rarely blow up outright. But they don&#8217;t respond to more hardware as easily &#8212; you can&#8217;t throw machines at a sort-merge join and expect a proportional speedup. (<a href="https://www.canadiandataguy.com/p/spark-join-strategies-explained-sort">Full mechanics here.</a>)</p><p>Shuffle hash joins do respond to more hardware. They&#8217;re less forgiving under memory pressure, but they scale with compute in a way sort-merge doesn&#8217;t. On an SLA-bound job, that tunability is worth more than the extra stability. (<a href="https://www.canadiandataguy.com/p/spark-join-strategies-explained-shuffle">Full breakdown here.</a>)</p><p><strong>Practice:</strong> If your streaming job has an SLA attached, treat join strategy as a lever you tune on purpose &#8212; not whatever Spark defaults to.</p><h2><strong>Rule 5: Set shuffle partitions to match cluster cores</strong></h2><blockquote><p>Highly discouraged, so unless you absolutely need to, you can just let auto do it for you </p><p>The only reason we set this is because we were heavily latency-bound and did not want to leave it to the auto algorithm. That means we were overly conservative.</p></blockquote><p>On join-heavy streams, <code>spark.sql.shuffle.partitions</code> drives task size and scheduler load.</p><ul><li><p>Too <strong>low</strong> &#8594; fat tasks, spill, OOM</p></li><li><p>Too <strong>high</strong> &#8594; tiny tasks, overhead, small files<br></p></li></ul><h2><strong><span>Recommended Companion Reads</span></strong></h2><p><span>Now that you&#8217;ve upgraded your hardware and parallelized your sinks, here is how to take your high-performance streaming architecture to the next level:</span></p><ol><li><p><strong><span>Optimize Your Compute Storage:</span></strong><span> Moving beyond default node disk settings can dramatically reduce streaming latency. Learn how to </span><a href="https://docs.databricks.com/aws/en/admin/clusters/manage-ssd"><span>Deploy and Manage Local SSDs on AWS</span></a><span> or upgrade your block storage setup with </span><a href="https://www.databricks.com/blog/2021/08/10/introducing-support-for-gp3-amazons-new-general-purpose-ssd-volume.html"><span>GP3 Support on Databricks</span></a><span> to instantly maximize write IOPS.</span></p></li><li><p><strong><span>Master Advanced Join Tactics:</span></strong><span> Choosing the wrong join strategy can derail your SLA during transaction spikes. Read our step-by-step guides on </span><a href="https://www.canadiandataguy.com/p/spark-join-strategies-explained-shuffle"><span>How Shuffle Hash Joins Scale</span></a><span> and why traditional </span><a href="https://www.canadiandataguy.com/p/spark-join-strategies-explained-sort"><span>Sort-Merge Joins Might Limit Your Streaming Performance</span></a><span>.</span></p></li><li><p><strong><span>Download the Implementation Notebook:</span></strong><span> Skip the boilerplate. Grab our ready-to-import </span><a href="https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks%2Fparallelize_for_each_batch.py"><span>Parallel ForeachBatch Script</span></a><span> from GitHub, and check out the walkthrough videos on </span><a href="https://www.youtube.com/watch?v=gur1oLe5t2o"><span>ForeachBatch Parallelization (Part 1)</span></a><span> and </span><a href="https://www.youtube.com/watch?v=n9jodzYq1e4&amp;t=5s"><span>ThreadPool Tuning (Part 2)</span></a><span>.</span></p></li></ol><h2><strong>FAQ</strong></h2><p><strong>When should I parallelize </strong><code>foreachBatch</code><strong> writes?</strong></p><p>When a single micro-batch writes to multiple independent sinks, such as a main table and a dead-letter path. Parallelization helps when sink writes are a meaningful share of batch duration and the driver can coordinate the extra jobs safely.</p><p><strong>Is caching inside </strong><code>foreachBatch</code><strong> usually a good idea?</strong></p><p>No. In many streaming pipelines, especially after joins or aggregations, Spark has already materialized shuffle output. Adding <code>.cache()</code> can increase memory use and GC overhead without enough reuse to justify it.</p><p><strong>Why does local NVMe matter for streaming jobs?</strong></p><p>Because shuffle, spill, and write-heavy micro-batches are sensitive to disk performance. Faster local storage can reduce batch-time variability and improve throughput for join-heavy workloads.</p><p><strong>Should I always change </strong><code>spark.sql.shuffle.partitions</code><strong> manually?</strong></p><p>No, almost never unless you absolutely can live without them. Auto-optimized settings may be sufficient. </p><p><strong>What does &#8220;success&#8221; mean for a latency-bound streaming job?</strong></p><p>Success is not just that the query keeps running. Success means the pipeline consistently finishes each micro-batch within the target latency window and prevents backlog from growing during spikes.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Stop Hand-Building Your Streaming Ops Layer]]></title><description><![CDATA[Structured Streaming gives you the engine. SDP gives you the pit crew. Here's why I reach for it first &#8212; and the two times I don't.]]></description><link>https://www.databricksters.com/p/stop-hand-building-your-streaming</link><guid isPermaLink="false">https://www.databricksters.com/p/stop-hand-building-your-streaming</guid><dc:creator><![CDATA[Canadian Data Guy]]></dc:creator><pubDate>Mon, 06 Jul 2026 15:02:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zRKU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Structured Streaming gives you raw power. Spark Declarative Pipelines (SDP) gives you that same power with a production-grade operations layer built on top, right out of the box.</p><p>I&#8217;ve spent the last 5 years designing and running thousands of streaming jobs. Here&#8217;s how I actually see the tradeoff.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zRKU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zRKU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 424w, https://substackcdn.com/image/fetch/$s_!zRKU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 848w, https://substackcdn.com/image/fetch/$s_!zRKU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!zRKU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zRKU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3107212,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/204534019?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zRKU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 424w, https://substackcdn.com/image/fetch/$s_!zRKU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 848w, https://substackcdn.com/image/fetch/$s_!zRKU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!zRKU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eeb6898-1d7c-47b5-9b2a-e482a7789077_2390x1792.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2><span>1. Zero-Downtime Runtime Upgrades</span></h2><p><span>This is one of the most under-appreciated advantages of SDP. With a classic Structured Streaming job on regular compute, a runtime upgrade looks like this:</span></p><ol><li><p><strong><span>Stop the job</span></strong><span> (downtime begins)</span></p></li><li><p><strong><span>Update the runtime version</span></strong><span> on the cluster</span></p></li><li><p><strong><span>Restart the job</span></strong></p></li><li><p><strong><span>Wait</span></strong><span> for startup and checkpoint recovery</span></p></li></ol><p><span>Every up</span>grade becomes a scheduled maintenance window. Because of this, teams constantly defer them, accumulate technical <span>debt, and eventually face a forced migration under pressure.</span></p><blockquote><p><strong><span>With SDP, your pipelines are versionless.</span></strong><span> Databricks manages the runtime lifecycle for you. New compute spins up in the background, your pipeline migrates over automatically, and there&#8217;s no restart sequence to plan aro</span>und. If you are running a few dozen streaming pipelines, this alone eliminates an entire category of operational toil.</p></blockquote><div><hr></div><h2>2. Infrastructure You Don&#8217;t Babysit</h2><ul><li><p><strong>Serverless SDP:</strong> There is no cluster to configure. SDP auto-scales, provisions, and tears down compute automatically. You write the pipeline logic; the platform handles the rest. No sizing debates, no idle costs between micro-batches, and SLA-backed startup times.</p></li><li><p><strong>Classic SDP:</strong> If you aren&#8217;t on serverless, you still get <strong>Enhanced Autoscaling</strong>. This feature is purpose-built for streaming, right-sizing your clusters based on actual backlog and throughput rather than rough guesses.</p></li></ul><div><hr></div><h2>3. Rewind and Recover, Instead of Rebuild</h2><p>Production pipelines break. Bad data ships, an unexpected code change causes corruption, or a bug slips through code review. On raw Structured Streaming, recovery usually means manually resetting checkpoints or reprocessing everything from scratch.</p><p><strong>SDP&#8217;s Rewind API changes that story.</strong> With a single operation, you can restore a pipeline&#8212;including table data, source offsets, and operator state together&#8212;to a clean point in time, apply your fix, and resume.</p><ul><li><p>No checkpoint deletion required.</p></li><li><p>No full refreshes needed.</p></li><li><p>The question shifts from <em>&#8220;How long until we&#8217;re caught up?&#8221;</em> to <em>&#8220;Rewind, fix, resume.&#8221;</em></p></li></ul><p><em>(Note: This is currently available on request&#8212;reach out via Databricks to get access.)</em></p><div><hr></div><h2>4. Quality, Dependencies, and Schema: Native, Not Bolted On</h2><p>On raw Structured Streaming, you have to build and maintain operational guardrails yourself. On SDP, they come baked into the platform:</p><ul><li><p><strong>Data Quality:</strong> Declare <code>@dp.expect</code> rules directly in your pipeline code. Choose whether to <code>warn</code>, <code>drop</code>, or <code>fail</code> per rule. Violations are tracked and visualized in the pipeline UI rather than being silently dropped.</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;a714b45f-3cb0-440c-9649-0e0fd524030f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">@dp.expect("valid_order_id", "order_id IS NOT NULL")
@dp.expect_or_drop("positive_amount", "amount &gt; 0")
@dp.expect_or_fail("no_duplicates", "COUNT(*) = COUNT(DISTINCT order_id)")
def silver_orders():
    return dlt.read_stream("bronze_orders")
</code></pre></div><ul><li><p><strong>Multi-Table Dependencies:</strong> SDP automatically resolves the full DAG (Directed Acyclic Graph) on its own. You don&#8217;t need an external orchestrator for pipeline-internal chains.</p></li><li><p><strong>Schema Evolution:</strong> Unexpected or changed fields automatically route to a <code>rescue</code> column instead of crashing your production run.</p></li><li><p><strong>CDC (Change Data Capture):</strong> The <code>APPLY CHANGES INTO</code> syntax gives you SCD Type 1/2 out of the box&#8212;no hand-written, complex <code>MERGE</code> logic required.</p></li></ul><p>Add a visual pipeline graph UI, a queryable event log, and row-level lineage through Unity Catalog.</p><div><hr></div><h2>5. When Structured Streaming Is Still the Right Call</h2><p>While you should default to SDP, two situations are worth reconsidering:</p><ul><li><p><strong>Library Compatibility:</strong> Your pipeline depends on third-party or custom libraries that don&#8217;t play well with serverless compute environments. Classic compute gives you full control over the environment.</p></li><li><p><strong>An Actual SDP Ceiling:</strong> You&#8217;ve hit a hard, documented limitation for your specific use case, rather than a hypothetical one.</p></li></ul><p>For everything else, choose <span>SDP first.</span></p><h2>The Bottom Line</h2><p>SDP doesn&#8217;t replace Structured Streaming&#8212;it removes the operational tax most teams end up paying anyway by building this layer poorly by hand. You ship faster, operate with less toil, and keep raw Structured Streaming as an escape hatch for the rare cases that genuinely need lower-level engine control.</p><p>If you are evaluating SDP for the first time or weighing whether to migrate existing Structured Streaming pipelines, talk to your Databricks account team. They can walk through your specific architecture and help you identify where the operational wins are real versus theoretical.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wk_h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wk_h!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 424w, https://substackcdn.com/image/fetch/$s_!Wk_h!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 848w, https://substackcdn.com/image/fetch/$s_!Wk_h!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 1272w, https://substackcdn.com/image/fetch/$s_!Wk_h!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wk_h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png" width="663" height="456" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:456,&quot;width&quot;:663,&quot;resizeWidth&quot;:663,&quot;bytes&quot;:63120,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/204534019?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wk_h!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 424w, https://substackcdn.com/image/fetch/$s_!Wk_h!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 848w, https://substackcdn.com/image/fetch/$s_!Wk_h!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 1272w, https://substackcdn.com/image/fetch/$s_!Wk_h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa52b2d2d-8e28-48bd-8c8e-6bed1261dc83_663x456.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>FAQ</h2><h5>What is SDP?</h5><p>SDP stands for <strong>Spark Declarative Pipelines</strong>. It provides a declarative way to build data pipelines with built-in operations, quality enforcement, dependency management, and observability.</p><h5>How is SDP different from Structured Streaming?</h5><p>Structured Streaming is the underlying core engine. SDP builds <em>on top</em> of it, adding production-focused capabilities like managed upgrades, pipeline orchestration, quality rules, and native monitoring.</p><h5>Is SDP only useful for large teams?</h5><p>No. Smaller teams often benefit even more because SDP removes the heavy operational lifting that smaller engineering teams don&#8217;t have the time or staffing to manage manually.</p><h5>Is SDP only useful for streaming workloads?</h5><p>No. While it is exceptionally strong for streaming and incremental pipelines, it also works seamlessly for multi-hop data pipelines that combine both batch and streaming patterns.</p><h5>Does SDP replace orchestration tools?</h5><p>For dependencies <em>inside</em> a single pipeline, often yes. SDP manages execution order and downstream dependencies automatically, reducing the need for an external orchestrator within that specific pipeline boundary.</p>]]></content:encoded></item><item><title><![CDATA[Why Your information_schema Queries Are Killing the Metastore]]></title><description><![CDATA[And the 3 Functions to Use Instead]]></description><link>https://www.databricksters.com/p/why-your-information_schema-queries</link><guid isPermaLink="false">https://www.databricksters.com/p/why-your-information_schema-queries</guid><dc:creator><![CDATA[Canadian Data Guy]]></dc:creator><pubDate>Tue, 30 Jun 2026 15:01:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!isGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sometimes engineers want to check if a table exists before writing or merging data. To do this, they often loop through a list of tables and query <code>information_schema.tables</code>.</p><div><hr></div><h2>&#10060; The Anti-Pattern: Full Catalog Scans in a Loop</h2><p>Here is the code that can causes pipelines to hit metastore rate limits or fail entirely:</p><pre><code><code># This forces a full scan of the entire metastore catalog on every execution
spark.read.table("catalog.information_schema.tables") \
    .filter((col("table_schema") == my_schema) &amp; (col("table_name") == my_table))
</code></code></pre><h3><span>The Technical Bottlene</span>ck</h3><p>The <code>information_schema</code> is built for wide-scope catalog introspection&#8212;such as <span>auditing, broad governance mapping, or checking metadata across an entire environment at once.</span></p><p><span>When you chain a </span><code>.filter()</code><span> in Spark </span><strong><span>after</span></strong><span> loading </span><code>information_schema.tables</code><span>, there is </span><strong><span>zero predicate pushdown</span></strong><span> to the underlying metastore database. The metast</span>ore is forced <span>to enumerate and return </span><em><span>every single object</span></em><span> inside that catalog to the Spark engine, leaving Spark to do the actual row filtering client-side in memory.</span></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!isGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!isGN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 424w, https://substackcdn.com/image/fetch/$s_!isGN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 848w, https://substackcdn.com/image/fetch/$s_!isGN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 1272w, https://substackcdn.com/image/fetch/$s_!isGN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!isGN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg" width="1456" height="863" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:863,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5561,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/svg+xml&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203766763?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!isGN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 424w, https://substackcdn.com/image/fetch/$s_!isGN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 848w, https://substackcdn.com/image/fetch/$s_!isGN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 1272w, https://substackcdn.com/image/fetch/$s_!isGN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F555d1579-2cf7-46e9-8ad7-2e34682e62ef_1080x640.svg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>The Solution: Use True Point-Lookup APIs</span></h2><p><span>If you only need to verify the existence of a single table, you must bypass catalog-wide views and target a single metadata record directly.</span></p><h3><span>Option 1: Programmatic Check via </span><code>tableExists()</code><span> (Recommended)</span></h3><p><span>For pure automation pipelines, the native Spark catalog API handles this efficiently without compiling a heavy relational query pl</span>an. It hits a single metadata record instantly.</p><pre><code><code># Target a single metadata record directly
exists = spark.catalog.tableExists("catalog.schema.table")
</code></code></pre><h3><span>Option 2: SQL Scoped Check via </span><code>SHOW TABLES</code></h3><p><span>If you are working strictly within raw SQL strings, limit your query scope using an explicit schema filter an</span>d a wildcard match rather than querying the entire catalog.</p><pre><code><code>-- Fast, schema-scoped point lookup
SHOW TABLES IN catalog.schema LIKE 'table_name';
</code></code></pre><h3>Option 3: Structural Validation via <code>DESCRIBE TABLE</code></h3><p>Often, you don&#8217;t just need to know if a table exists&#8212;you need to ensure its current column layout matches what your engine expects. You can leverage structural evaluation enclosed in an exception block.</p><pre><code><code>from pyspark.sql.utils import AnalysisException

# Validates existence and structure simultaneously 
try:
    spark.sql("DESCRIBE TABLE catalog.schema.table")
    exists = True
except AnalysisException:
    # Catch AnalysisException specifically. A bare 'except' turns 
    # permission errors and transient timeouts into false "table missing" signals.
    exists = False
</code></code></pre><h2>When Do You Actually Need <code>information_schema</code>?</h2><p>If your objective is broad discovery&#8212;like mapping end-to-end data lineage across the entire workspace&#8212;using <code>information_schema</code> is completely appropriate. However, you should follow this single operational rule: <strong>Read it exactly once at the top of the job, cache it, and filter from memory.</strong></p><pre><code><code># Execute exactly one metastore scan total
all_tables = spark.read.table("catalog.information_schema.tables").cache()

# Filter from the cached memory layer as many times as needed
bronze_tables = all_tables.filter(col("table_schema") == "bronze")
silver_tables = all_tables.filter(col("table_schema") == "silver")
</code></code></pre><div><hr></div><h2>The Ultimate Fix: Cut the Check Entirely</h2><p>Pre-flight existence checks usually exist because a pipeline doesn&#8217;t know whether to create a new table or append to an existing one. If you are using Delta Lake, Delta already solves this for you.</p><p>Using <code>.mode("append")</code> will automatically create the table on the first write, and seamlessly append to it on every run after:</p><pre><code><code>df.write \
  .format("delta") \
  .mode("append") \
  .saveAsTable("catalog.schema.target_table")
</code></code></pre><p>SQL pipelines get the exact same guarantee from a single DDL line at setup:</p><pre><code><code>CREATE TABLE IF NOT EXISTS catalog.schema.target_table (
    id INT,
    data STRING,
    event_time TIMESTAMP
) USING DELTA;

INSERT INTO catalog.schema.target_table SELECT * FROM staging_view;
</code></code></pre><blockquote><p><strong>The Bottom Line:</strong> When you see an existence check before a write in a code review, ask what it&#8217;s actually guarding against. Usually, there&#8217;s no good answer&#8212;and removing it will save your metastore from a world of hurt.</p></blockquote>]]></content:encoded></item><item><title><![CDATA[The Mbappé Effect: Deploying French Stuff on Serverless GPUs]]></title><description><![CDATA[Proprietary foundation models are great for a wide array of use cases, but many companies have legitimate need for an OSS model.]]></description><link>https://www.databricksters.com/p/the-mbappe-effect-deploying-french</link><guid isPermaLink="false">https://www.databricksters.com/p/the-mbappe-effect-deploying-french</guid><dc:creator><![CDATA[Austin]]></dc:creator><pubDate>Tue, 30 Jun 2026 14:03:24 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/553c1a8a-b1a7-41bd-bfc1-1050d84f017c_1600x922.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>Proprietary foundation models are great for a wide array of use cases, but many companies have legitimate need for an OSS model. Sometimes it&#8217;s a security concern, other times it&#8217;s about tuning and optimizing for task specific performance, and sometimes it just comes down to cost or latency SLAs. Whatever your reason, you want to serve an LLM to custom GPU model serving, and I&#8217;m here to help. </span></p><p><span>And before you accuse me of writing clickbait titles that have little to do with the content of the blog (I would never do that), I assure you that Mbapp&#233; will be mentioned no fewer than 4 times.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8-fu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8-fu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8-fu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8-fu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8-fu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8-fu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:381698,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/204202223?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8-fu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8-fu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8-fu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8-fu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F827e4bcd-efa9-446d-96e4-90af5df3159c_1600x1200.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">&lt;INSERT CLICKBAIT IMAGE HERE&gt;</figcaption></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.databricksters.com/subscribe?"><span>Subscribe now</span></a></p><p>The landscape of LLMs evolves so rapidly that it&#8217;s nearly impossible to keep up with every model, let alone every dependency change that may be required by them. <span>With AI Runtime and Custom GPU Model Serving, it&#8217;s trivially easy to serve the latest OSS models, because we can edit the versions at the outset, just as we would any other notebook. </span></p><p>In honor of their World Cup victory, let&#8217;s use a Mistral model: <code>Devstral-Small-2-24B-Instruct-2512</code><span>, a great mid-sized model for SWE tasks that deploys easily on Databricks custom GPU model serving.</span></p><p><span>Mbapp&#233;.</span></p><p><span>Most of these package versions are shipped natively with AI Runtime v5, but just to underscore my above point, I&#8217;ll set them here as well:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ca3491a1-aa02-463e-b565-03e6e2270fc4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">%pip install databricks-sdk==0.102.0 vllm==0.13.0 transformers==4.57.6 mistral_common openai==2.17.0 opencv-python-headless==4.12.* mlflow==3.12.0 hf_transfer==0.1.9
%restart_python</code></pre></div><p><span>We&#8217;ll now set the wd to local disk and configure the endpoint. We set </span><code>SKIP_LOCAL_SERVE_TEST=True</code><span> unless we want to load the whole thing into RAM here in the serverless notebook. That would work on a single node H100, but not on an A10. We can also set the max model length here if we don&#8217;t need to service long prompts and want to conserve VRAM during serving. We&#8217;ll also set the location in UC we want to save this to, the endpoint name, and the sizing here as well.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;759bb310-d93e-49dd-88b3-686b77d49775&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Set working directory to local disk (/Workspace doesn't support large files).
import os, tempfile
workdir = tempfile.mkdtemp()
os.chdir(workdir)
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
# Hugging Face model to download.
MODEL_REPO_ID = "mistralai/Devstral-Small-2-24B-Instruct-2512"
ARTIFACTS_PATH = "devstral"        # Local dir the weights download to
SERVED_MODEL_NAME = "devstral"     # Name vLLM exposes the model under.

# Devstral 2 Small FP8 (~24 GB) won't fit an A10
SKIP_LOCAL_SERVE_TEST = True
MAX_MODEL_LEN = 24576
GPU_MEMORY_UTILIZATION = 0.90

# Allowlisted ports for Serverless GPU notebooks are 3000-3999. Model Serving requires 8080.
LOCAL_PORT = 3080
SERVING_PORT = 8080

# Unity Catalog destination
UC_MODEL_NAME = "catalog.schema.devstral_small_2_24b_fp8"

# Serving endpoint configuration.
ENDPOINT_NAME = "austin-devstral-24b-h100"
WORKLOAD_SIZE = "Small"
SCALE_TO_ZERO_ENABLED = False   # GPU_XLARGE/H100 does NOT support scale-to-zero in Beta

from databricks.sdk.service.serving import ServingModelWorkloadType
WORKLOAD_TYPE = ServingModelWorkloadType.GPU_XLARGE</code></pre></div><p><span>Mbapp&#233;.</span></p><p><span>Now we can download the model:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ed2947e9-50a4-43d9-abb8-00f25d4aaab3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from huggingface_hub import snapshot_download

snapshot_download(
   repo_id=MODEL_REPO_ID,
   local_dir=ARTIFACTS_PATH,
)</code></pre></div><p>And log it to our custom entry point and print the model URI in case we want to reference that later. This will take a few minutes. Note that we do NOT download from HF in the serving container.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;1b9473c2-fef1-4d95-a850-88d44fc59d09&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import mlflow
from mlflow.pyfunc.model import ChatModel, ChatCompletionResponse

# Required placeholder. Serving runs the entrypoint, not python_model.predict.
class LLMModel(ChatModel):
   def predict(self, context, messages, params):
       return ChatCompletionResponse.from_dict({"choices": []})

mlflow.set_registry_uri("databricks-uc")

model_info = mlflow.pyfunc.log_model(
   name=SERVED_MODEL_NAME,
   python_model=LLMModel(),
   artifacts={
       "model_dir": ARTIFACTS_PATH,
   },
   metadata={
       "task": "llm/v1/chat",
       "entrypoint": entrypoint(SERVING_PORT),
   },
   extra_pip_requirements=[
       "mlflow==3.12.0",
   ],
)
model_info.model_uri</code></pre></div><p>Now we register the model. Note that <code>env_pack="databricks_model_serving"</code> packages the env and the bundled weights for Serverless Optimized Deployments. This step will take like 15 minutes.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ca33a911-3c75-4826-8573-eaa5698fdbe7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">model_version = mlflow.register_model(model_info.model_uri, UC_MODEL_NAME, env_pack="databricks_model_serving")
print(f"Registered {UC_MODEL_NAME} version {model_version.version}")</code></pre></div><p><span>Mbapp&#233;.</span></p><p><span>Our model is now ready for deployment. This cell provisions its own </span><code>GPU_XLARGE</code><span> (i.e. H100) serving endpoint independent of this A10 notebook.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;63ef1a07-97c9-4ad6-b140-3f3f386bf057&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from databricks.sdk import WorkspaceClient
from datetime import timedelta
from databricks.sdk.service.serving import EndpointCoreConfigInput, ServedEntityInput

config = EndpointCoreConfigInput(
   name=ENDPOINT_NAME,
   served_entities=[
       ServedEntityInput(
           entity_name=UC_MODEL_NAME,
           entity_version=str(model_version.version),
           workload_type=WORKLOAD_TYPE,
           workload_size=WORKLOAD_SIZE,
           scale_to_zero_enabled=SCALE_TO_ZERO_ENABLED,
       )
   ]
)

w = WorkspaceClient()
w.serving_endpoints.create_and_wait(name=ENDPOINT_NAME, config=config, timeout=timedelta(minutes=45))</code></pre></div><p>And you&#8217;ve done it. With that, all that&#8217;s left to do is query the endpoint to confirm it&#8217;s working.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;2837d8f5-bf37-4c74-817a-ca5341edb74f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from databricks.sdk import WorkspaceClient
from databricks.sdk.service.serving import ChatMessage, ChatMessageRole

w = WorkspaceClient()
resp = w.serving_endpoints.query(
   name=ENDPOINT_NAME,
   messages=[ChatMessage(role=ChatMessageRole.USER, content="Hi, will Mbapp&#233; lead France to another World Cup victory? And by extension make Mistral the world favorite OSS LLM for SWE tasks?")],
)
print(resp.choices[0].message.content)</code></pre></div><p><span>Happy coding,</span></p><p><span>-Austin</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Stop Guessing AI Agent Integrations: The Databricks Decision Tree]]></title><description><![CDATA[Discover essential patterns for secure, scalable AI agent integration with Databricks, covering OBO, token federation, and multi-agent strategies. Get artifact checklists and CLI commands.]]></description><link>https://www.databricksters.com/p/stop-guessing-ai-agent-integrations</link><guid isPermaLink="false">https://www.databricksters.com/p/stop-guessing-ai-agent-integrations</guid><dc:creator><![CDATA[Aravind Nallan]]></dc:creator><pubDate>Wed, 24 Jun 2026 16:28:29 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f7c9d4c1-f15e-4256-8b42-c56e67fc83c7_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><span>The Problem</span></h2><p><span>Business users do most of their work outside of a data platform: sales reps draft proposals in Outlook, operations leads run their day in Teams, finance models everything in Excel.Yet the answers to most of their questions sit in Databricks: pipeline metrics, supply chain status, support SLAs, financial actuals, etc.</span></p><p><span>That gap between &#8220;where data lives&#8221; and &#8220;where people work&#8221; used to be bridged by reports and dashboards. Now the bridge is AI agents: assistants embedded in Teams, Outlook, Excel, Copilot Hub, Microsoft Foundry projects, or an in-house chat app. The user asks a question in their flow of work. The agent calls back to Databricks. The answer returns with proper governance applied.</span></p><p><span>The pattern is simple in theory. In practice, the integration involves a few interlocking decisions: which Databricks resources to wire up, how authentication flows, whether the agent supports MCP or only REST, whether users and Databricks share a Microsoft Entra tenant, and what governance controls you need. Get them wrong and you lose all the benefits of governance inherent to Databricks.</span></p><p><span>This post walks you through a decision tree to get to the right architecture, with the exact set of artifacts to share between the Databricks side and the agent side at the end.</span></p><h2><span>Architecture Components</span></h2><p><span>Before walking the tree, here are some quick definitions of the different Databricks functions we will be dealing with.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U0NG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U0NG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 424w, https://substackcdn.com/image/fetch/$s_!U0NG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 848w, https://substackcdn.com/image/fetch/$s_!U0NG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 1272w, https://substackcdn.com/image/fetch/$s_!U0NG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U0NG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:197754,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!U0NG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 424w, https://substackcdn.com/image/fetch/$s_!U0NG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 848w, https://substackcdn.com/image/fetch/$s_!U0NG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 1272w, https://substackcdn.com/image/fetch/$s_!U0NG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa903ad22-7ec2-4d2f-9a3f-fbe71ebeac09_1574x868.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>Question 1: Which Databricks Resources Need to Be Connected?</span></h2><p><span>Databricks provides four managed MCP servers out of the box. Each connects a different resource type with its own URL pattern and OAuth scope.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b00I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b00I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 424w, https://substackcdn.com/image/fetch/$s_!b00I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 848w, https://substackcdn.com/image/fetch/$s_!b00I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 1272w, https://substackcdn.com/image/fetch/$s_!b00I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b00I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png" width="1456" height="772" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:772,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:200456,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b00I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 424w, https://substackcdn.com/image/fetch/$s_!b00I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 848w, https://substackcdn.com/image/fetch/$s_!b00I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 1272w, https://substackcdn.com/image/fetch/$s_!b00I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32198225-5e6b-490f-86c0-e670a148a3ee_1572x834.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>All four are governed by Unity Catalog permissions and visible in </span><strong><span>AI Gateway &gt; MCPs</span></strong><span>.</span></p><p><strong><span>Note on Vector Search:</span></strong><span> The managed MCP server works directly with indexes that use Databricks-managed embeddings; you send plain text and get plain text results. If you use self-managed embeddings, wrap the index in a Model Serving endpoint (for example, via VectorSearchRetrieverTool in a deployed agent) or a UC Function, and call that from the agent instead.</span></p><h2><span>Question 2: How Many Databricks Endpoints, and Does the Agent Support Multiple Tool Endpoints?</span></h2><p><span>This determines whether you connect a single URL or several.</span></p><h3><span>Path A: Single resource</span></h3><p><span>Connect one managed MCP URL directly. Simplest path: no orchestration needed.</span></p><h3><span>Path B: Multiple resources, agent supports multiple endpoints</span></h3><p><span>Create one OAuth application with combined scopes and hand the agent team all the MCP URLs. The same OAuth token works across all of them.</span></p><p><span>OAuth app scopes (combine as needed):</span></p><p><code>vector-search genie unity-catalog sql offline_access</code></p><p><span>Or use all-apis for blanket access (less restrictive).</span></p><p><span>One OAuth flow, one token, multiple MCP servers. The user authenticates once and the token carries the correct scopes for every resource.</span></p><h3><span>Path C: Multiple resources, agent supports only one endpoint</span></h3><p><span>Two options.</span></p><p><strong><span>Option 1: Supervisor Agent (recommended for most cases)</span></strong></p><p><span>Register each Databricks resource as a sub-agent of an AgentBricks Multi-Agent Supervisor Agent. The external agent connects to one Model Serving endpoint.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PsNU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PsNU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 424w, https://substackcdn.com/image/fetch/$s_!PsNU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 848w, https://substackcdn.com/image/fetch/$s_!PsNU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 1272w, https://substackcdn.com/image/fetch/$s_!PsNU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PsNU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png" width="1456" height="459" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f952ed30-1dea-435b-a141-055fb3879532_1574x496.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:459,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:106124,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PsNU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 424w, https://substackcdn.com/image/fetch/$s_!PsNU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 848w, https://substackcdn.com/image/fetch/$s_!PsNU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 1272w, https://substackcdn.com/image/fetch/$s_!PsNU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff952ed30-1dea-435b-a141-055fb3879532_1574x496.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The external agent connects to a single REST endpoint: POST https://&lt;workspace&gt;/serving-endpoints/&lt;supervisor&gt;/invocations. The supervisor&#8217;s LLM handles routing: it reads the user&#8217;s query and delegates to the right sub-agent based on descriptions you provide.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pi4z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pi4z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 424w, https://substackcdn.com/image/fetch/$s_!pi4z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 848w, https://substackcdn.com/image/fetch/$s_!pi4z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 1272w, https://substackcdn.com/image/fetch/$s_!pi4z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pi4z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png" width="1456" height="496" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:496,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:117348,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pi4z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 424w, https://substackcdn.com/image/fetch/$s_!pi4z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 848w, https://substackcdn.com/image/fetch/$s_!pi4z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 1272w, https://substackcdn.com/image/fetch/$s_!pi4z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a973314-71d2-4f87-b7e0-ce278a3cfeed_1572x536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>Option 2: Custom MCP Server (Databricks App)</span></strong></p><p><span>Deploy a Databricks App that proxies to multiple managed MCP servers behind a single URL: https://&lt;app-url&gt;/mcp.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yCVV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yCVV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 424w, https://substackcdn.com/image/fetch/$s_!yCVV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 848w, https://substackcdn.com/image/fetch/$s_!yCVV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 1272w, https://substackcdn.com/image/fetch/$s_!yCVV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yCVV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png" width="1456" height="380" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:380,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78296,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yCVV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 424w, https://substackcdn.com/image/fetch/$s_!yCVV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 848w, https://substackcdn.com/image/fetch/$s_!yCVV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 1272w, https://substackcdn.com/image/fetch/$s_!yCVV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F544cf676-009b-4c63-9499-5122f0e119b0_1570x410.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>Decision rule:</span></strong><span> Minimal code with LLM-driven routing goes to Supervisor Agent. Lightweight deterministic proxying goes to Custom MCP App.</span></p><h2><span>Question 3: What Authentication Model Is Required?</span></h2><p><span>This is the most consequential decision. It determines how identity flows from the external agent to Databricks.</span></p><h3><span>The key question: does the end user&#8217;s identity need to flow through to Databricks?</span></h3><p><strong><span>Yes: On-Behalf-Of (OBO) Authentication</span></strong></p><p><span>The external agent authenticates on behalf of each individual user. Databricks receives the user&#8217;s identity, and Unity Catalog permissions are enforced per-user.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ABg0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ABg0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 424w, https://substackcdn.com/image/fetch/$s_!ABg0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 848w, https://substackcdn.com/image/fetch/$s_!ABg0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 1272w, https://substackcdn.com/image/fetch/$s_!ABg0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ABg0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png" width="1456" height="384" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:384,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:109734,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ABg0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 424w, https://substackcdn.com/image/fetch/$s_!ABg0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 848w, https://substackcdn.com/image/fetch/$s_!ABg0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 1272w, https://substackcdn.com/image/fetch/$s_!ABg0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47f1b1f-0ddc-454a-b829-c032209c6789_1570x414.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>OBO-supported resources on Model Serving:</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bpmf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bpmf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 424w, https://substackcdn.com/image/fetch/$s_!Bpmf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 848w, https://substackcdn.com/image/fetch/$s_!Bpmf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 1272w, https://substackcdn.com/image/fetch/$s_!Bpmf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bpmf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png" width="1456" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131935,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bpmf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 424w, https://substackcdn.com/image/fetch/$s_!Bpmf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 848w, https://substackcdn.com/image/fetch/$s_!Bpmf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 1272w, https://substackcdn.com/image/fetch/$s_!Bpmf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb205e14-7134-4d69-83cf-66cb84e457bd_1570x728.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>If you need OBO access to resources outside this list (e.g., UC Volumes), deploy on Databricks Apps instead of Model Serving. Apps support additional OAuth scopes.</span></p><p><strong><span>No: Service Principal Authentication</span></strong></p><p><span>The external agent authenticates as a single identity (service principal). All queries run under that SP&#8217;s permissions.</span></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7tQh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7tQh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 424w, https://substackcdn.com/image/fetch/$s_!7tQh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 848w, https://substackcdn.com/image/fetch/$s_!7tQh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 1272w, https://substackcdn.com/image/fetch/$s_!7tQh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7tQh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png" width="1456" height="349" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:349,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86351,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7tQh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 424w, https://substackcdn.com/image/fetch/$s_!7tQh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 848w, https://substackcdn.com/image/fetch/$s_!7tQh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 1272w, https://substackcdn.com/image/fetch/$s_!7tQh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0ab886a-a30b-4787-99c5-a68a36a15c7a_1570x376.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3><span>Authentication Method Comparison</span></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Qgja!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Qgja!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 424w, https://substackcdn.com/image/fetch/$s_!Qgja!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 848w, https://substackcdn.com/image/fetch/$s_!Qgja!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 1272w, https://substackcdn.com/image/fetch/$s_!Qgja!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Qgja!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png" width="1456" height="494" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:494,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:110513,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Qgja!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 424w, https://substackcdn.com/image/fetch/$s_!Qgja!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 848w, https://substackcdn.com/image/fetch/$s_!Qgja!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 1272w, https://substackcdn.com/image/fetch/$s_!Qgja!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe95d9f90-9aa0-4941-8d15-a1cb73c1dd81_1568x532.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The question to ask the data security team: do different users need different access levels to the data behind these resources, or is a shared service account acceptable?</span></p><h2><span>Question 4: Are Users and Databricks in the Same Azure Tenant?</span></h2><p><span>This question only matters if you chose OBO or token federation in Q3. With a simple PAT or client credentials inside the same org, skip to Q5.</span></p><h3><span>Same tenant</span></h3><p><span>Standard OAuth flows work directly:</span></p><ul><li><p><span>Users already exist in Databricks via SCIM sync from the same Entra ID</span></p></li><li><p><span>OAuth authorization code flow authenticates users against the same tenant</span></p></li><li><p><span>No federation policy needed</span></p></li></ul><h3><span>Different tenants (cross-tenant)</span></h3><p><span>Common in enterprise scenarios: users live in Tenant A (corporate Entra ID), but Databricks is associated with Tenant B.</span></p><p><strong><span>Path A: Per-user OBO (users in external tenant need individual identity)</span></strong></p><ol><li><p><span>Create an account-wide federation policy:</span></p><ul><li><p><strong><span>Issuer:</span></strong><span> https://login.microsoftonline.com/&lt;external-tenant-id&gt;/v2.0</span></p></li><li><p><strong><span>Subject claim:</span></strong><span> sub or oid (to match users by their Entra ID object ID)</span></p></li><li><p><strong><span>Audience:</span></strong><span> Databricks account ID or custom value</span></p></li></ul></li><li><p><span>Provision users in Databricks. Federation validates identity but does NOT create accounts. Use SCIM sync from the external tenant, or provision manually.</span></p></li><li><p><span>Each user&#8217;s Entra ID token is exchanged for a Databricks token carrying their identity.</span></p></li><li><p><span>Unity Catalog permissions apply per-user.</span></p></li></ol><p><strong><span>Path B: Service Principal (automated agent, no per-user identity)</span></strong></p><ol><li><p><span>Create a Databricks service principal in your Databricks account.</span></p></li><li><p><span>Create a service principal federation policy:</span></p><ul><li><p><strong><span>Issuer:</span></strong><span> https://login.microsoftonline.com/&lt;external-tenant-id&gt;/v2.0</span></p></li><li><p><strong><span>Subject:</span></strong><span> The Entra ID app/service principal&#8217;s sub or oid claim</span></p></li><li><p><strong><span>Audience:</span></strong><span> Databricks account ID</span></p></li></ul></li><li><p><span>The external agent obtains a JWT from its own Entra ID tenant.</span></p></li><li><p><span>Exchanges it via:</span></p></li></ol><p><span>   </span><code>POST https://&lt;workspace&gt;/oidc/v1/token</code></p><p><code>   grant_type=urn:ietf:params:oauth:grant-type:token-exchange</code></p><p><code>   subject_token=&lt;entra-id-jwt&gt;</code></p><p><code>   subject_token_type=urn:ietf:params:oauth:token-type:jwt</code></p><ol><li><p><span>Zero Databricks secrets cross the tenant boundary.</span></p></li></ol><h3><span>Cross-tenant requirements checklist</span></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z-zu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z-zu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 424w, https://substackcdn.com/image/fetch/$s_!Z-zu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 848w, https://substackcdn.com/image/fetch/$s_!Z-zu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 1272w, https://substackcdn.com/image/fetch/$s_!Z-zu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z-zu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png" width="1456" height="609" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb723616-c024-4200-8cfc-e951d18c538c_1578x660.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:609,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:147124,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Z-zu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 424w, https://substackcdn.com/image/fetch/$s_!Z-zu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 848w, https://substackcdn.com/image/fetch/$s_!Z-zu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 1272w, https://substackcdn.com/image/fetch/$s_!Z-zu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb723616-c024-4200-8cfc-e951d18c538c_1578x660.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>Question 5: What Governance Controls Are Needed?</span></h2><p><span>AI Gateway is the enterprise control plane that sits between MCP servers and external agents. It provides the operational controls needed in production.</span></p><h3><span>AI Gateway Capabilities for MCP</span></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1Rld!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Rld!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 424w, https://substackcdn.com/image/fetch/$s_!1Rld!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 848w, https://substackcdn.com/image/fetch/$s_!1Rld!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 1272w, https://substackcdn.com/image/fetch/$s_!1Rld!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Rld!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png" width="1456" height="1112" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1112,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:278599,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1Rld!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 424w, https://substackcdn.com/image/fetch/$s_!1Rld!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 848w, https://substackcdn.com/image/fetch/$s_!1Rld!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 1272w, https://substackcdn.com/image/fetch/$s_!1Rld!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed0eb8e9-2600-40c0-b0d3-3ad8d2bfd648_1574x1202.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Find it in: Workspace sidebar &gt; </span><strong><span>AI Gateway</span></strong><span> &gt; </span><strong><span>MCPs</span></strong><span>.</span></p><h3><span>Recommended governance setup</span></h3><ol><li><p><strong><span>Rate limiting</span></strong><span>: set conservative limits initially, increase based on observed usage.</span></p></li><li><p><strong><span>Usage tracking</span></strong><span>: enable from day one to establish a baseline.</span></p></li><li><p><strong><span>Audit logging</span></strong><span>: required for any integration where per-user identity flows through (OBO).</span></p></li><li><p><strong><span>IP allowlisting</span></strong><span>: restrict MCP server access to known external agent IPs.</span></p></li><li><p><strong><span>Scope-limited OAuth</span></strong><span>: use granular scopes (vector-search, genie) instead of all-apis for production.</span></p></li></ol><h2><span>Question 6: What Information Needs to Be Exchanged Between Teams?</span></h2><p><span>Setting up the integration requires a handshake between the Databricks team and the external agent team. Here is exactly what each side provides.</span></p><h3><span>From the Databricks side</span></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!niTp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!niTp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 424w, https://substackcdn.com/image/fetch/$s_!niTp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 848w, https://substackcdn.com/image/fetch/$s_!niTp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 1272w, https://substackcdn.com/image/fetch/$s_!niTp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!niTp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png" width="1456" height="1260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1260,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:314254,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!niTp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 424w, https://substackcdn.com/image/fetch/$s_!niTp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 848w, https://substackcdn.com/image/fetch/$s_!niTp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 1272w, https://substackcdn.com/image/fetch/$s_!niTp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F851fcdc5-4257-4bdf-a1a0-09ac06099054_1572x1360.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3><span>From the external agent team</span></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!L0jR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!L0jR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 424w, https://substackcdn.com/image/fetch/$s_!L0jR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 848w, https://substackcdn.com/image/fetch/$s_!L0jR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 1272w, https://substackcdn.com/image/fetch/$s_!L0jR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!L0jR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png" width="1456" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:201930,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!L0jR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 424w, https://substackcdn.com/image/fetch/$s_!L0jR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 848w, https://substackcdn.com/image/fetch/$s_!L0jR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 1272w, https://substackcdn.com/image/fetch/$s_!L0jR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f8c9e8-3550-46e3-ae8f-09d5ff02abbc_1574x830.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><span>Setup steps on the Databricks side</span></h3><ol><li><p><span>Get redirect URLs and IPs from the external agent team.</span></p></li><li><p><span>Create the OAuth app: Account Console &gt; Settings &gt; App Connections &gt; Add connection.</span></p></li></ol><ul><li><p><span>Name: descriptive (e.g., foundry-mcp-client)</span></p></li><li><p><span>Redirect URLs: from agent team</span></p></li><li><p><span>Client type: as specified by agent team</span></p></li><li><p><span>Scopes: combine as needed</span></p></li></ul><ol><li><p><span>Allowlist IPs in the workspace IP access list (if applicable).</span></p></li><li><p><span>Grant UC permissions: ensure users or SPs have access to the vector search index, Genie space, UC functions, or tables.</span></p></li><li><p><span>Share credentials: send the agent team the client ID, endpoints, and MCP server URLs.</span></p></li><li><p><span>Configure AI Gateway: set up rate limits, enable usage tracking.</span></p></li></ol><p><span>Automation-friendly OAuth app creation via CLI:</span></p><p><code>databricks account custom-app-integration create --json &#8216;{</code></p><p><code>  &#8220;name&#8221;: &#8220;foundry-mcp-client&#8221;,</code></p><p><code>  &#8220;redirect_urls&#8221;: [&#8221;https://&lt;redirect-from-agent-team&gt;&#8221;],</code></p><p><code>  &#8220;confidential&#8221;: true,</code></p><p><code>  &#8220;scopes&#8221;: [&#8221;vector-search&#8221;, &#8220;genie&#8221;, &#8220;unity-catalog&#8221;, &#8220;offline_access&#8221;]</code></p><p><code>}&#8217;</code></p><h2><span>The Decision Tree at a Glance</span></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rY7L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rY7L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 424w, https://substackcdn.com/image/fetch/$s_!rY7L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 848w, https://substackcdn.com/image/fetch/$s_!rY7L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 1272w, https://substackcdn.com/image/fetch/$s_!rY7L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rY7L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png" width="1456" height="1083" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1083,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:314101,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rY7L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 424w, https://substackcdn.com/image/fetch/$s_!rY7L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 848w, https://substackcdn.com/image/fetch/$s_!rY7L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 1272w, https://substackcdn.com/image/fetch/$s_!rY7L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32bb2cee-ef17-4aef-8bd2-482677d858ec_1860x1384.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3><span>Common Scenarios (Quick Paths)</span></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o3Et!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o3Et!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 424w, https://substackcdn.com/image/fetch/$s_!o3Et!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 848w, https://substackcdn.com/image/fetch/$s_!o3Et!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 1272w, https://substackcdn.com/image/fetch/$s_!o3Et!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o3Et!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png" width="1456" height="759" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:759,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:187347,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/203388920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!o3Et!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 424w, https://substackcdn.com/image/fetch/$s_!o3Et!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 848w, https://substackcdn.com/image/fetch/$s_!o3Et!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 1272w, https://substackcdn.com/image/fetch/$s_!o3Et!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55f5a06f-e2bb-45c0-97f1-09c6834c709b_1576x822.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>Closing Thoughts</span></h2><p><span>Working through these six questions before writing any code keeps you from over-engineering a Supervisor Agent when a single MCP URL would have done the job, or from committing to OBO before confirming that your agent framework supports authorization code flow.</span></p><p><span>If you take only one thing from this post: answer the cross-tenant question early. Tenant topology determines whether you need a federation policy, and federation policies require account admin access on the Databricks side. Discovering that requirement after you have built half the integration is painful.</span></p><p><span>Done well, none of this complexity ever reaches the user. They open Teams, ask &#8220;what was our on-time delivery rate in the Northeast last week,&#8221; and get back numbers that respect their permissions, their region, their role. They never see the OAuth flow, the supervisor routing, or the federation policy. They just get the answer where they were already working. That is the whole point.</span></p><h2><span>References</span></h2><ul><li><p><a href="https://docs.databricks.com/generative-ai/mcp/managed-mcp/"><span>Use Databricks managed MCP servers</span></a></p></li><li><p><a href="https://docs.databricks.com/generative-ai/mcp/connect-external-services/"><span>Connect non-Databricks clients to Databricks MCP servers</span></a></p></li><li><p><a href="https://docs.databricks.com/generative-ai/agent-framework/agent-authentication-model-serving/"><span>Authentication for AI agents (Model Serving)</span></a></p></li><li><p><a href="https://docs.databricks.com/dev-tools/auth/oauth-federation/"><span>OAuth token federation</span></a></p></li><li><p><a href="https://docs.databricks.com/dev-tools/auth/oauth-federation-policy/"><span>Configure a federation policy</span></a></p></li><li><p><a href="https://docs.databricks.com/generative-ai/agent-bricks/multi-agent-supervisor/"><span>Supervisor Agent (multi-agent system)</span></a></p></li><li><p><a href="https://docs.databricks.com/generative-ai/mcp/custom-mcp/"><span>Host custom MCP servers using Databricks Apps</span></a></p></li><li><p><a href="https://docs.databricks.com/generative-ai/agent-framework/multi-agent-apps/"><span>Build a multi-agent system on Databricks Apps</span></a></p></li><li><p><a href="https://docs.databricks.com/ai-gateway/"><span>Unity AI Gateway</span></a></p></li><li><p><a href="https://docs.databricks.com/generative-ai/agent-framework/unstructured-retrieval-tools/"><span>Connect agents to unstructured data (Vector Search)</span></a></p></li></ul><h2><span>Glossary</span></h2><p><strong><span>Term</span></strong></p><p><strong><span>Definition</span></strong></p><p><strong><span>MCP</span></strong></p><p><span>Model Context Protocol, an open standard for connecting AI agents to tools and data sources</span></p><p><strong><span>OBO</span></strong></p><p><span>On-behalf-of authentication: the agent acts with the end user&#8217;s identity and permissions</span></p><p><strong><span>Token Federation</span></strong></p><p><span>Exchanging an external IdP JWT for a Databricks OAuth token without storing Databricks secrets</span></p><p><strong><span>AI Gateway</span></strong></p><p><span>Databricks&#8217; central governance layer for LLM endpoints, MCP servers, and coding agents</span></p><p><strong><span>Supervisor Agent</span></strong></p><p><span>A multi-agent orchestrator that routes queries to specialized sub-agents behind a single endpoint</span></p>]]></content:encoded></item><item><title><![CDATA[Reclaim Spend from Idle Databricks Apps]]></title><description><![CDATA[A daily governance workflow combining System Tables and OpenTelemetry]]></description><link>https://www.databricksters.com/p/reclaim-spend-from-idle-databricks</link><guid isPermaLink="false">https://www.databricksters.com/p/reclaim-spend-from-idle-databricks</guid><dc:creator><![CDATA[Sonakshi Pandey]]></dc:creator><pubDate>Tue, 09 Jun 2026 15:49:24 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2018d97b-ca3d-4c4d-9127-7832e01143ad_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>TL;DR:  A workflow that finds and stops idle apps</strong></h2><p style="text-align: justify;">Databricks Apps adoption can outpace the visibility into what&#8217;s running, who owns each app, and what it&#8217;s costing. A workspace can quickly accumulate hundreds of apps across business teams - some delivering ongoing value, many running 24/7 long after anyone last touched them, with the compute bill climbing alongside.</p><p style="text-align: justify;"> Effective app management requires two complementary signals:</p><ul><li><p style="text-align: justify;"><strong>System tables</strong> : for inventory, ownership, lifecycle events, and cost attribution</p></li><li><p style="text-align: justify;"><strong>OpenTelemetry export to Unity Catalog</strong> : for request-level usage and idle detection [At the time this blog was published, App telemetry is in  <a href="https://docs.databricks.com/aws/en/release-notes/release-types">Public Preview</a>.]</p></li></ul><p style="text-align: justify;">Used together, these answer the full set of questions a platform team needs: what do we have, who owns it, what does it cost, and is anyone using it? This post walks through both layers and shows how to combine them into a daily workflow that includes automated idle-app shutdown.</p><h2><strong>Pillar 1: Governance and Cost Attribution with System Tables</strong></h2><p style="text-align: justify;"><a href="https://docs.databricks.com/aws/en/admin/system-tables/">System tables</a> provide the foundation for Apps governance. Four tables, combined, answer most operational questions:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b_5j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b_5j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 424w, https://substackcdn.com/image/fetch/$s_!b_5j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 848w, https://substackcdn.com/image/fetch/$s_!b_5j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 1272w, https://substackcdn.com/image/fetch/$s_!b_5j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b_5j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png" width="1404" height="426" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:426,&quot;width&quot;:1404,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:66360,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/200801782?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b_5j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 424w, https://substackcdn.com/image/fetch/$s_!b_5j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 848w, https://substackcdn.com/image/fetch/$s_!b_5j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 1272w, https://substackcdn.com/image/fetch/$s_!b_5j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e1853d7-2646-4b3e-82d6-1ab1da03649d_1404x426.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Questions this layer answers</strong></h3><p style="text-align: justify;">System tables provide the core signals needed to understand the operational footprint of Databricks Apps across a workspace:</p><ul><li><p><strong>Ownership :</strong> Creation events help identify the original app owner, while ACL change events help surface who currently has management access.</p></li><li><p><strong>Cost :</strong> Usage data from system.billing.usage, combined with pricing data from system.billing.list_prices, makes it possible to estimate app cost over time.</p></li><li><p><strong>Orphaned Apps: </strong> Apps with no apparent current manager can be flagged for review as potentially orphaned.</p></li><li><p><strong>Deletion history :</strong> Delete events provide a historical view of app removals, including timing and actor.</p></li><li><p><strong>Recreate cycles:</strong> Creation and deletion history can reveal create &#8594; delete &#8594; recreate patterns that may indicate experimentation, churn, or unstable ownership.</p></li></ul><p style="text-align: justify;">System tables answer <em>what do we have and what does it cost.</em> The next dimension - <em>is anyone actually using it</em> - requires a usage signal.</p><h2><strong>Pillar 2: Usage Signals with OpenTelemetry</strong></h2><p style="text-align: justify;"><a href="https://docs.databricks.com/aws/en/dev-tools/databricks-apps/observability">Databricks Apps support native OpenTelemetry</a> export to Unity Catalog. Three telemetry tables can be configured for an app, but they differ in what is required to populate them:</p><ul><li><p style="text-align: justify;">otel_logs - request logs and platform-emitted auth events. <strong>Populated automatically once OTel export is configured and the app is redeployed.</strong> No code changes required.</p></li><li><p style="text-align: justify;">otel_metrics - request counts, latencies, and other application-level metrics. <strong>Only populated after custom instrumentation is added to the app code</strong> (e.g. using the OpenTelemetry SDK in the application).</p></li><li><p style="text-align: justify;">otel_spans - distributed traces. <strong>Only populated after custom instrumentation is added to the app code.</strong></p></li></ul><p style="text-align: justify;">For governance use cases - and idle detection in particular - otel_logs is the primary signal, and the one that works out of the box. Each request lands in otel_logs with an app.name attribute and timestamp, providing per-request activity data that is independent of compute state.</p><p style="text-align: justify;">If teams add custom instrumentation later, otel_metrics and otel_spans open up deeper analysis (latency SLOs, error budgets, user journey traces), but they are not required for the workflow described in this post.</p><h3><strong>Enabling OTel export on an app</strong></h3><p style="text-align: justify;">Enablement is a single asynchronous update call per app, using the <strong>:createUpdate</strong> endpoint with an explicit <strong>update_mask</strong>.</p><p style="text-align: justify;">&#8252;&#65039;Do not use the direct <strong>PATCH /api/2.0/apps/{name}</strong> endpoint for this purpose because it performs a full replacement of the app object, which will silently wipe resources, env_vars, and permission attachments when those fields aren&#8217;t included in the request body. :createUpdate with <strong>update_mask: &#8220;telemetry_export_destinations&#8221;</strong> ensures only that field is modified.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;e02f76c5-30da-4009-b878-8a5b6f60ddfb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">
def patch_telemetry(app_name):
   """Enables OpenTelemetry export to Unity Catalog for the specified Databricks App."""
   payload = {
       "app": {
           "telemetry_export_destinations": [
               {"unity_catalog": TELEMETRY_TABLES}
           ]
       },
       "update_mask": "telemetry_export_destinations"
   }
   return requests.post(
       f"{workspace_url}/api/2.0/apps/{app_name}:createUpdate",
       headers=headers,
       json=payload,
   )</code></pre></div><p>Looping this over <code>GET /api/2.0/apps</code> lets you enable telemetry across the full app fleet. After each app is redeployed, otel_logs begins populating automatically once the app receives traffic, while <strong>otel_metrics </strong>and<strong> otel_spans</strong> remain empty until the application code is instrumented.</p><h3><strong>What OTel adds beyond billing data</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7KpS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7KpS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 424w, https://substackcdn.com/image/fetch/$s_!7KpS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 848w, https://substackcdn.com/image/fetch/$s_!7KpS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 1272w, https://substackcdn.com/image/fetch/$s_!7KpS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7KpS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png" width="1406" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9276db53-0b61-40f1-9110-1451c6349564_1406x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1406,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:92720,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/200801782?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7KpS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 424w, https://substackcdn.com/image/fetch/$s_!7KpS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 848w, https://substackcdn.com/image/fetch/$s_!7KpS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 1272w, https://substackcdn.com/image/fetch/$s_!7KpS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9276db53-0b61-40f1-9110-1451c6349564_1406x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Both layers are needed for confident operational decisions. An app may be running and accruing DBUs while no user has touched it for weeks.</p><h3>&#128680; Caveats and Operational Considerations with OTel</h3><ul><li><p style="text-align: justify;"><strong>Cold-start gap after OTel enablement. </strong>The workflow trusts otel_logs to mean &#8216;this app had no requests in the activity window.&#8217; If OTel export was just enabled, no telemetry has had time to accumulate - every app looks idle, and a first-day run will flag every app for shutdown. Don&#8217;t act on the workflow&#8217;s recommendations until telemetry has been collecting for at least as long as your idle-detection window.</p></li><li><p style="text-align: justify;"><strong>PII in telemetry. OTel auto-instrumentation captures request URLs</strong>, query parameters, headers, and request bodies. If apps pass user data through URLs, it will land in <strong>otel_logs</strong>. Apply row-level security on telemetry tables from day one.</p></li><li><p style="text-align: justify;"><strong>Staggered enablement and redeployment.</strong> Patching telemetry config and redeploying causes a user-visible app restart. For workspaces with hundreds of apps, schedule the enablement in off-hours batches and stage tier-1 apps separately to minimize disruption.</p></li><li><p style="text-align: justify;"><strong>Explicit protection for critical apps.</strong> Maintain a <strong>PROTECTED_APPS</strong> list in the shutdown job. Incident-response tools, on-call dashboards, and tier-1 business apps should be hard-coded as exempt regardless of activity level.</p></li><li><p><strong>Telemetry table retention.otel_logs</strong> grows quickly at scale. Set retention windows and <strong>VACUUM</strong> schedules on day one, partition by date, and monitor storage cost. Otherwise, telemetry storage will offset the savings from idle-app shutdown.</p></li></ul><h2><strong>Governance using System Tables and OTel</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QyEE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QyEE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 424w, https://substackcdn.com/image/fetch/$s_!QyEE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 848w, https://substackcdn.com/image/fetch/$s_!QyEE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 1272w, https://substackcdn.com/image/fetch/$s_!QyEE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QyEE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QyEE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 424w, https://substackcdn.com/image/fetch/$s_!QyEE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 848w, https://substackcdn.com/image/fetch/$s_!QyEE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 1272w, https://substackcdn.com/image/fetch/$s_!QyEE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b6fe97-81aa-424e-b8cf-37ed7a3bdb0b_2048x1117.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: justify;"><strong>With both System tables and OTel metrics in place, identifying and stopping idle apps becomes a 5-step workflow.</strong></p><h3>Idle Detection and Shutdown in 5 simple steps</h3><h4>Step 1: Find apps with recent activity</h4><p style="text-align: justify;">Query the OpenTelemetry logs for any app that has had user requests or auth events in the activity window. A 7-day window is a safe production default; tighten only after observing baseline traffic patterns.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;d5f29edf-77ed-4350-8b25-bb03ee6b7929&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">WITH activity AS (
SELECT DISTINCT
  COALESCE(
    service_name,
    variant_get(resource.attributes, '$["app.name"]', 'STRING')
  ) AS app_name
FROM my_catalog.my_schema.otel_logs
WHERE `time` &gt;= current_timestamp() - INTERVAL 7 DAYS
)
SELECT app_name FROM activity WHERE app_name IS NOT NULL</code></pre></div><h4>Step 2: Compute idle candidates</h4><p>Combine the activity set with the live app inventory (<code>GET /api/2.0/apps</code>) and apply three safety filters: only <code>ACTIVE</code> apps are candidates, anything in <code>PROTECTED_APPS</code> is exempt regardless of activity, and any app younger than <code>MIN_AGE_DAYS</code> is skipped so newly-deployed apps have room to ramp.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;c6b43d93-30b3-409e-a4fd-453a639f57ad&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from datetime import datetime, timezone
from dateutil.parser import parse as parse_ts

PROTECTED_APPS = {
    "oncall-dashboard",
    "incident-router",
    "exec-monitoring",
}
MIN_AGE_DAYS = 7   # grace period for newly-deployed apps

active = {
    row["app_name"]
    for row in spark.sql("&lt;Step 1 SQL&gt;").collect()
}

candidates = []
for app in list_all_apps():
    state = (app.get("compute_status") or {}).get("state")
    age_days = (datetime.now(tz=timezone.utc) - parse_ts(app["create_time"])).days
    if (
        state == "ACTIVE"
        and app["name"] not in active
        and app["name"] not in PROTECTED_APPS
        and age_days &gt;= MIN_AGE_DAYS
    ):
        candidates.append(app["name"])
</code></pre></div><h4>Step 3: Resolve owners and notify</h4><p style="text-align: justify;">This step combines both pillars: query system.access.audit (Pillar 1) to derive a per-app ownership table, then resolve owners for the candidate set generated from OpenTelemetry logs (Pillar 2), and send a 7-day warning to each candidate&#8217;s owner. The notification mechanism is intentionally a placeholder - Slack, email, or a Databricks Apps inbox notification all work.  The audit trail produced in Step 4 is what matters.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b871d7cc-7adf-467a-a0fd-4b54d2812a13&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Build the per-app ownership table from System table system.access.audit.
# Run this once during setup; refresh weekly via a separate job.

spark.sql("""
  CREATE OR REPLACE TABLE apps_ownership AS
  WITH created_latest AS (
    SELECT
      get_json_object(request_params['app'], '$.name') AS app_name,
      workspace_id,
      user_identity.email AS creator_email,
      ROW_NUMBER() OVER (
        PARTITION BY get_json_object(request_params['app'], '$.name'),
                     workspace_id
        ORDER BY event_time DESC
      ) AS rn
    FROM system.access.audit
    WHERE service_name = 'apps'
      AND action_name = 'createApp'
      AND event_date &gt;= current_date() - INTERVAL 365 DAYS
  )
  SELECT app_name, workspace_id, creator_email FROM created_latest WHERE rn = 1
""")

# Daily run: scope the ownership lookup to today's candidate set
spark.createDataFrame(
    [(name,) for name in candidates],
    ["app_name"],
).createOrReplaceTempView("idle_app_candidates")

owners = (
    spark.sql("SELECT app_name, creator_email FROM apps_ownership WHERE app_name IN (SELECT app_name FROM idle_app_candidates) AND workspace_id = '&lt;your workspace_id&gt;'")
    .toPandas()
    .set_index("app_name")["creator_email"]
    .to_dict()
)

for name in candidates:
    owner = owners.get(name, ADMIN_FALLBACK_EMAIL)
    notify(  # replace with your Slack / email / paging integration
        to=owner,
        subject=f"App '{name}' scheduled for shutdown in 7 days",
        body=(
            "No activity detected in the last 7 days. "
            "Touch the app or add it to PROTECTED_APPS to keep it running."
        ),
    )</code></pre></div><h4>Step 4: Reconcile the journal and enforce the warning period</h4><p style="text-align: justify;">This step does two things to the journal:</p><ul><li><p style="text-align: justify;">First, it sweeps stale rows - any journal entry whose app showed activity in the current window is deleted, since the warning&#8217;s premise no longer holds. This includes rows from previously-stopped apps whose owners have restarted them, ensuring the reactivation is acknowledged and a fresh warning cycle begins if the app later goes idle again.</p></li><li><p style="text-align: justify;">Second, it MERGEs today&#8217;s idle candidates into the journal. The actual stop call in Step 5 only fires for apps that have been on the journal for at least WARNING_DAYS and are still idle on the next run, so this guarantees no app is stopped without a full warning period elapsing, and an app that bounces between active and idle starts each new idle cycle with a fresh <code>warning_sent_at</code>.</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;53a1ec59-fe27-45cd-a03f-238faab2d98a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Sweep stale journal entries first.
spark.createDataFrame(
    [(n,) for n in active],
    ["app_name"],
).createOrReplaceTempView("active_apps_today")

spark.sql("""
  DELETE FROM shutdown_journal
  WHERE app_name IN (SELECT app_name FROM active_apps_today)
""")

# Then MERGE today's warnings (idle_app_candidates was registered in Step 3).
spark.sql("""
  MERGE INTO shutdown_journal target
  USING (
    SELECT app_name, current_timestamp() AS warning_sent_at
    FROM idle_app_candidates
  ) source
  ON target.app_name = source.app_name
  WHEN NOT MATCHED THEN INSERT (app_name, warning_sent_at, stopped_at, status)
    VALUES (source.app_name, source.warning_sent_at, NULL, NULL)
""")</code></pre></div><h4>Step 5: Stop expired candidates with an audit record</h4><p style="text-align: justify;">For each app that&#8217;s been in the journal at least <code>WARNING_DAYS</code> and is still idle and still not protected, call the stop endpoint and write the outcome back to the journal. </p><p style="text-align: justify;">&#8252;&#65039; <strong>Stop, don&#8217;t delete</strong> - stopping is reversible, deletion is not.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;899c0648-8a0d-4247-9310-19bd9eb079cb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">expired = spark.sql("""
  SELECT app_name
  FROM shutdown_journal
  WHERE warning_sent_at &lt;= current_timestamp() - INTERVAL 7 DAYS
    AND stopped_at IS NULL
""").collect()

for row in expired:
    name = row["app_name"]
    if name in active or name in PROTECTED_APPS:
        continue  # last-second safety check
    r = requests.post(
        f"{workspace_url}/api/2.0/apps/{name}/stop",
        headers=headers,
        timeout=60,
    )
    spark.sql(
        "UPDATE shutdown_journal "
        "SET stopped_at = current_timestamp(), status = :status "
        "WHERE app_name = :name",
        args={"status": str(r.status_code), "name": name},
    )
</code></pre></div><h4>Orchestration </h4><p style="text-align: justify;">Wrap the five steps in a single <a href="https://docs.databricks.com/aws/en/jobs/configure-job#create-a-new-job">Databricks Job</a>:</p><ul><li><p style="text-align: justify;"><strong>Cadence:</strong> daily, off-hours (e.g., 02:00 UTC) so restart impact lands outside business hours</p></li><li><p style="text-align: justify;"><strong>Identity:</strong> a dedicated service principal (apps-governance-sp) with the minimum permissions:</p><ul><li><p style="text-align: justify;"><code>SELECT</code> on otel_logs and apps_ownership</p></li><li><p style="text-align: justify;"><code>SELECT / INSERT / UPDATE /DELETE</code> on shutdown_journal</p></li><li><p style="text-align: justify;"><code>CAN_MANAGE</code> on the apps you intend to govern (required to call /stop)</p></li></ul></li><li><p style="text-align: justify;"><strong>Parameters:</strong> <code>DRY_RUN</code> (default true for the first two weeks of rollout), <code>MIN_AGE_DAYS</code> (default 7), WARNING_DAYS (default 7)</p></li><li><p style="text-align: justify;"><strong>Alerts:</strong> route job failures to the platform-team channel. A silent failure of this job means orphaned compute keeps running - the cost of missed runs is real money.</p></li><li><p style="text-align: justify;">Materialization: keep <strong>apps_ownership</strong> as a Delta table (not a view), refreshed weekly by a separate job. Daily shutdown runs read from the pre-materialized object in milliseconds instead of re-scanning audit logs every time. Apps younger than <code>MIN_AGE_DAYS</code> can&#8217;t be candidates anyway, so weekly refresh is sufficient.</p></li></ul><h3>Apps monitoring dashboard</h3><p>The daily shutdown job is one side of the governance loop, an <strong>automated action</strong>. The other side is <strong>decision support</strong>: <a href="https://docs.databricks.com/aws/en/dashboards">Databricks dashboard</a> that surfaces the same signals to platform reviewers, app owners, and executives.</p><p> Built on the same two pillars, an account-wide dashboard answers the questions the platform team gets asked every week:</p><ul><li><p><strong>Inventory KPIs:</strong> total apps, active vs stopped vs error states, OTel enablement coverage</p></li><li><p><strong>Cost trend:</strong> weekly DBU consumption per app, with hover-to-USD via system.billing.list_prices</p></li><li><p><strong>Usage signals:</strong> apps used in the last 7 days, apps unused in the last 7 days (the shutdown candidate set), broken down by workspace</p></li><li><p><strong>Lineage/ownership:</strong> owner email, last access, last admin action - all derived from system.access.audit</p></li></ul><p>Admins see a single account-wide view instead of N per-workspace views. The visual interface turns raw signal into context that lets people decide <em>which</em> apps to keep, retire, or invest in - without manually correlating audit logs, OTel data, and billing tables.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mR4G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mR4G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 424w, https://substackcdn.com/image/fetch/$s_!mR4G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 848w, https://substackcdn.com/image/fetch/$s_!mR4G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 1272w, https://substackcdn.com/image/fetch/$s_!mR4G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mR4G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png" width="1456" height="1172" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1172,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mR4G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 424w, https://substackcdn.com/image/fetch/$s_!mR4G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 848w, https://substackcdn.com/image/fetch/$s_!mR4G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 1272w, https://substackcdn.com/image/fetch/$s_!mR4G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee42077-5be7-453e-929a-c4bbadbaf3c1_2048x1649.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Summary</strong></h2><p style="text-align: justify;">A complete governance posture for Databricks Apps combines three building blocks:</p><ol><li><p style="text-align: justify;"><strong>System tables</strong> for inventory, ownership, cost, lifecycle, and compliance reporting.</p></li><li><p style="text-align: justify;"><strong>OpenTelemetry `otel_logs` export</strong> for request-level usage and idle detection - available out of the box once OTel is configured. otel_metrics and otel_spans add deeper application-level observability when teams add custom instrumentation.</p></li><li><p style="text-align: justify;"><strong>Automated controls</strong> - a daily job that identifies idle apps, notifies owners, and stops stale apps.</p></li></ol><p>System tables answer <em>what do we have,</em> and <em>what does it cost. </em>OpenTelemetry tells you whether<em> anyone is using it.</em> <strong>Together, the two enable platform teams to support broad Apps adoption while keeping costs predictable and ownership accountable.</strong></p><h2><strong>FAQ</strong></h2><p><strong>1. Why aren&#8217;t system tables alone enough to find idle apps?</strong></p><p>System tables show inventory, ownership, lifecycle events, and billing, but they do not reliably show whether users are actively interacting with an app. For idle detection, you need request-level activity from telemetry.</p><p><strong>2. Which OpenTelemetry table is most important for this workflow?</strong></p><p><code>otel_logs</code> is the key table because it captures request and auth activity without requiring application code changes, once telemetry export is configured and the app is redeployed.</p><p><strong>3. Why should the workflow stop apps instead of deleting them?</strong></p><p>Stopping is reversible and preserves the app for later use. Deletion is permanent and introduces unnecessary operational risk for an automated governance process.</p><p><strong>4. What is the biggest rollout risk when enabling telemetry?</strong></p><p>If you run idle detection immediately after enabling telemetry, every app can appear idle because no activity history exists yet. Wait at least as long as your idle window before enforcing shutdown actions.</p><p><strong>5. How should critical apps be handled?</strong></p><p>Maintain an explicit protected-apps allowlist so business-critical or operational apps are never stopped automatically, regardless of recent activity.</p><p><strong>6. How often should the idle-app workflow run?</strong></p><p>A daily off-hours run is a practical default. It gives enough frequency to control spend without creating unnecessary operational churn.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Three decisions that make or break your production agent]]></title><description><![CDATA[A practitioner's framework for the three forks in the road that actually matter &#8212; tackle them in whatever order makes sense for your team]]></description><link>https://www.databricksters.com/p/three-decisions-that-make-or-break</link><guid isPermaLink="false">https://www.databricksters.com/p/three-decisions-that-make-or-break</guid><dc:creator><![CDATA[Austin]]></dc:creator><pubDate>Thu, 28 May 2026 15:02:02 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f0ff43c1-d35b-4c84-9a9b-6995c1bf4890_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1><strong>TL;DR</strong></h1><ul><li><p>Most production agent failures come from three architectural decisions: <strong>data serving path</strong>, <strong>data access abstraction</strong>, and <strong>observability</strong>.</p></li><li><p>Start with the <strong>simplest serving path that meets latency needs</strong>: use SQL Warehouse first, optimize table layout and queries, then add session caching or OLTP only if measurements justify it.</p></li><li><p>Put all data access behind <strong>one execution interface</strong> like <code>execute_sql(...)</code> so you can centralize routing, caching, retries, parameterization, and safety controls.</p></li><li><p>Treat observability as a launch requirement: <strong>trace tool calls, store traces in queryable tables, and add a synthetic heartbeat</strong> to measure true end-to-end freshness and latency.</p></li><li><p>The winning pattern is not &#8220;most advanced architecture first&#8221;; it is <strong>measure, optimize the hot path, and preserve the ability to swap components without rewriting the agent</strong>.</p><p></p></li></ul><h2>The Framework</h2><p>You can stand up an agent in an afternoon. Standing one that holds latency SLAs under real concurrency is a different exercise &#8212; one most teams underestimate.</p><p>The good news: most production agents need to talk to data and they will eventually run into the same three forks in the road. Pick well on each, and most of the other problems become tractable. Pick poorly, and you&#8217;ll be rebuilding under pressure.</p><p>This is a practitioner&#8217;s framework for the three decisions. They&#8217;re independent &#8212; you can tackle them in any order, and most teams end up working on all three in parallel. What matters is that you&#8217;ve made a deliberate call on each one.</p><h2><strong>Decision 1: How will the agent fetch data?</strong></h2><p>The instinct here is to jump to the most complex serving pattern before tuning the simplest one. Resist it.</p><p>You have three realistic options:</p><ul><li><p><strong>Option A &#8212; Direct SQL Warehouse Serverless.</strong> Your tool calls go straight to a SQL warehouse. No new infrastructure, no reverse ETL. Worth knowing: Serverless  keeps a remote result cache that survives warehouse shutdowns, so repeat queries stay fast even after the warehouse scales to zero. The fastest thing to ship.</p></li><li><p><strong>Option B &#8212; In-session caching.</strong> If your agent makes repeated queries against the same dataset within a single session, fetch it once and cache it in the session so subsequent calls don&#8217;t re-hit the warehouse. The implementation can be as simple as a dict or a DataFrame in the agent process, or an embedded SQL engine like DuckDB if you need to issue varied SQL against the cached data. The decision is about <em>recognizing intra-session repetition</em>, not about picking a cache library.</p></li><li><p><strong>Option C &#8212; Lakebase (OLTP).</strong> You reverse-ETL the tables the agent reads into Databricks Lakebase, a managed Postgres, and the agent queries Postgres instead of Delta. Sub-50ms point lookups. Highest performance ceiling, highest operational cost. Lakebase also doubles as a place to store online features for sub-second feature lookups and can persist agent session state &#8212; if you&#8217;re already running it for those reasons, the marginal cost of using it as your serving layer drops.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wtgf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wtgf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 424w, https://substackcdn.com/image/fetch/$s_!wtgf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 848w, https://substackcdn.com/image/fetch/$s_!wtgf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 1272w, https://substackcdn.com/image/fetch/$s_!wtgf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wtgf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png" width="1456" height="852" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:852,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wtgf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 424w, https://substackcdn.com/image/fetch/$s_!wtgf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 848w, https://substackcdn.com/image/fetch/$s_!wtgf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 1272w, https://substackcdn.com/image/fetch/$s_!wtgf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37a31680-b4eb-426a-950a-ae33d1eeeeea_1462x856.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>The mistake I see most:</strong> teams pattern-match on &#8220;we need low latency&#8221; and jump to Option C before they&#8217;ve measured Option A. They end up running a reverse-ETL pipeline they didn&#8217;t need.</p><p>Here&#8217;s the order that actually works:</p><ol><li><p><strong>Start with Option A.</strong> Get the agent end-to-end on SQL Warehouse/ DBSQL  Serverless. Size the warehouse sensibly &#8212; Medium for dev, Large for prod is a reasonable starting point &#8212; X-Large gets expensive fast and is rarely the right place to begin. Start with min 1, max 3&#8211;4 clusters, then watch the query history; if queries are getting queued, add a cluster. Keep the warehouse warm if you can&#8217;t tolerate cold-start tail latency on the first query after a scale-down.</p></li><li><p><strong>Tune the simple path before adding a complex one.</strong> Did you actually optimize the layout of the table the agent reads? Are your queries hitting the right clustering keys? Are you using native parameterized queries, not inline params? In our experience, half of the &#8220;we need OLTP&#8221; cases evaporate once the table layout is right.</p></li><li><p><strong>Only then evaluate B or C.</strong> The decision turns on access pattern, not vibes. If a single session issues several tool calls that hit the same underlying data, Option B&#8217;s in-session caching pays for itself &#8212; the trigger is repetition within a session, not session length. If your agent makes single point lookups with hard sub-second SLAs across thousands of concurrent sessions, Option C is the answer.</p></li></ol><p>A note on what actually matters for performance: it&#8217;s the engine serving the agent and the layout of the specific table that engine reads &#8212; nothing else. Medallion labels are organizational naming, not architecture. The agent doesn&#8217;t care what tier a table sits in; it cares whether the table it queries is laid out for fast reads. If your agent reads from a Delta table via SQL Warehouse/DBSQL, that t<a href="https://www.canadiandataguy.com/p/optimizing-delta-lake-tables-liquid">able&#8217;s clustering and file layout determine your latency.</a> If your agent reads from Lakebase, the Delta layout upstream is mostly irrelevant; what matters is the Postgres indexing and the sync freshness. Either way: optimize whatever the serving engine actually reads at query time, and let Predictive Optimization handle upstream tables that aren&#8217;t in the agent&#8217;s hot path. Avoid materialized views with non-deterministic refresh behavior anywhere in that hot path.</p><p>For Delta tables specifically, use <strong>liquid clustering</strong> and put real thought into the clustering keys. Sortable types make great keys &#8212; integers, dates, and timestamps in particular, because they have clean min/max statistics that let Delta skip files efficiently. String keys can work, but they&#8217;re far more sensitive to encoding choices (lexicographic ordering, padding, casing) and tend to underperform. Pick keys that match the actual filter patterns the agent will hit &#8212; usually some tenant or entity ID plus a time dimension &#8212; and verify the agent&#8217;s queries are pruning files the way you expect by reading the query profile.</p><p><strong>Quick tip:</strong> If you&#8217;re merging into a table continuously &#8212; say every 5 seconds &#8212; schedule explicit liquid clustering at a regular cadence (every 30&#8211;60 minutes). Otherwise you&#8217;re relying on Predictive Optimization to catch up on its own schedule, and for hot, agent-facing tables that&#8217;s usually not fast enough.</p><p><strong>Force yourself to write down, before you build:</strong></p><ul><li><p>The p95 latency budget per agent tool call, end-to-end, not per-query.</p></li><li><p>Average and p90 queries per agent session. If it&#8217;s two, the cache barely helps. If it&#8217;s ten, it&#8217;s a no-brainer.</p></li><li><p>The size of the largest object the agent will scan. Some OLTP stores have hard per-instance ceilings; if you&#8217;re past them, the architecture has to bend.</p></li></ul><h2><strong>Decision 2: How will you expose data to the agent?</strong></h2><p>Some tool in your agent that needs data &#8212;they needs a way to talk to the data plane. The instinct is to give each tool its own SQL connector when needed, its own connection management, its own retry logic. Don&#8217;t.</p><p><strong>Use one execution interface.</strong> A single function &#8212; call it execute_sql(query, max_staleness:int = 60) &#8212; that every data-bearing tool calls. Routing, caching, parameterization, observability, retries: all internal to that one function. Tools pass SQL, get results back, and don&#8217;t know or care whether the query went to SQL Warehouse, an in-memory cache, or Lakebase.</p><p>Without abstraction each tool manages its own connector, retries, parameters. With execute_sql One place to route, optimize, and enforce safety. Without the abstraction, every tool reinvents connection management. With it, you change one function to swap engines, tune queries, or harden security.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yy77!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yy77!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 424w, https://substackcdn.com/image/fetch/$s_!yy77!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 848w, https://substackcdn.com/image/fetch/$s_!yy77!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 1272w, https://substackcdn.com/image/fetch/$s_!yy77!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yy77!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png" width="1456" height="785" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:785,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yy77!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 424w, https://substackcdn.com/image/fetch/$s_!yy77!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 848w, https://substackcdn.com/image/fetch/$s_!yy77!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 1272w, https://substackcdn.com/image/fetch/$s_!yy77!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feac67b8f-2159-495f-9688-006f2a3877e4_1588x856.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is the single highest-leverage abstraction you can put in your agent. Three reasons:</p><ul><li><p><strong>One place to optimize.</strong> Tuning one query path improves every tool simultaneously. Tuning nine separate paths means you&#8217;ll tune three and forget the rest.</p></li><li><p><strong>One place to swap implementations.</strong> When you decide to add caching (Decision 1, Option B) or move to Lakebase (Option C), no tool code changes. You rewrite execute_sql and you&#8217;re done.</p></li><li><p><strong>One place to enforce safety.</strong> Parameterization, query limits, allowed-table lists &#8212; all enforced once.</p></li></ul><p>On safety specifically, two things to bake in from day one.</p><p><strong>Native parameterized queries, always.</strong> If you&#8217;re on databricks-sql-connector &gt;= 3.0, use paramstyle=&#8221;named&#8221;. Inline parameters bypass server-side caching, won&#8217;t survive a future connector release, and open up injection vectors. </p><p><strong>Keep sensitive identifiers out of the model&#8217;s parameter space.</strong> If your agent operates on a specific tenant, customer, or dataset, the model should not be the thing choosing which tenant to query. Use a factory/closure pattern: bind the tenant ID into the tool at construction time, so the model can only call the tool &#8212; it can&#8217;t substitute a different tenant via prompt injection. Save the parameterized version for arguments the model genuinely should control, like a column name to inspect.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;16c7b980-494c-4610-9b76-58796595ff26&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def make_detect_missing_data(tenant_id: str):
    @tool
    def detect_missing_data(column_name: str) -&gt; str:
        """Detect null values in a column."""
        return _run(tenant_id, column_name)  # tenant_id closed over
    return detect_missing_data</code></pre></div><p>Compare that to the naive pattern where tenant_id is a tool parameter the LLM fills in. The naive version is one cleverly-worded user message away from a tenant-isolation incident.</p><p><strong>Let the caller declare freshness tolerance.</strong> execute_sql should accept a max_staleness parameter &#8212; the maximum age of cached data the caller will tolerate for this query. If max_staleness is generous (say, 60 seconds), the query runner is free to serve from the in-session cache, the SQL Warehouse remote result cache, or a Lakebase replica with known lag. If it&#8217;s zero, the runner bypasses caches and goes to the source of truth. Th<strong>is pushes the consistency-vs-latency tradeoff to the call site, where the tool author actually knows whether eventual consistency is acceptable</strong>. Without it, you end up either over-caching (and serving stale answers in places that can&#8217;t tolerate them) or under-caching (and paying full warehouse latency for queries that didn&#8217;t need fresh data).</p><h2><strong>Decision 3: How will you know the agent is working?</strong></h2><p>If you can&#8217;t see what your agent is doing in production, you don&#8217;t have an agent &#8212; you have a black box that occasionally answers questions. M<strong>ost teams get tracing late, after the first incident. Don&#8217;t be that team.</strong></p><p><strong>Use MLflow tracing, and route it to Unity Catalog.</strong> MLflow&#8217;s tracing is OpenTelemetry-compatible. Link your experiment to a Unity Catalog trace location with </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;a88208ae-20e8-4577-aded-81379008657b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">syntax trace_location=UnityCatalog(
catalog_name=catalog_name,
schema_name=schema_name,
table_prefix=table_prefix)</code></pre></div><p>and your traces land as Delta tables in UC that you can query with SQL. You&#8217;ll thank yourself the first time someone asks &#8220;show me every slow trace for tenant X in the last 24 hours.&#8221; Without UC-backed traces, that&#8217;s an afternoon of grepping logs. With them, it&#8217;s a SQL query.</p><p><strong>Instrument the right boundaries.</strong> At minimum: input validation, tool dispatch, each tool&#8217;s SQL execution, and the model call itself. If your agent framework supports autolog, lean on it &#8212; but verify it&#8217;s actually emitting the spans you expect. Auto-instrumentation has bugs and gets disabled by version mismatches. When autolog isn&#8217;t trustworthy in your stack, fall back to manual mlflow.start_span() for the boundaries that matter. Don&#8217;t trace everything; trace the things you&#8217;d want to filter on during an incident &#8212; tenant_id, query_pattern, tool_name.</p><p><strong>Add a synthetic heartbeat.</strong> Inject a known record at the source on a fixed cadence &#8212; every 30 or 60 seconds &#8212; and measure when it shows up at the agent. This is the one honest measure of end-to-end latency. System tables and per-stage metrics will tell you each component is healthy while the whole pipeline is silently 90 seconds behind. A heartbeat catches that immediately. It&#8217;s about a hundred lines of code; build it before you scale.</p><p><strong>Set a latency budget per stage and defend it.</strong> Source &#8594; ingestion &#8594; transformation &#8594; serving table &#8594; agent. Every stage gets a number. Every stage has an SLO. When something breaks, you know which stage to look at without guessing. The budget also forces an honest design conversation: if your end-to-end target is 30 seconds and CDC alone takes 12, you don&#8217;t have 18 seconds for everything else &#8212; you have to decide what to cut.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RWN2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RWN2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 424w, https://substackcdn.com/image/fetch/$s_!RWN2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 848w, https://substackcdn.com/image/fetch/$s_!RWN2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 1272w, https://substackcdn.com/image/fetch/$s_!RWN2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RWN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png" width="1456" height="799" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:799,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RWN2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 424w, https://substackcdn.com/image/fetch/$s_!RWN2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 848w, https://substackcdn.com/image/fetch/$s_!RWN2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 1272w, https://substackcdn.com/image/fetch/$s_!RWN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1169f96-4b28-41a0-82ea-b67e412ad3b8_1586x870.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Three observability tactics in one picture: a per-stage budget you can defend, trace spans landing in queryable storage, and a heartbeat that measures end-to-end the same way a user would.</p><h2><strong>What this looks like in practice</strong></h2><p>Start simple. Ship Option A for serving. Wrap data access in execute_sql. Turn on MLflow tracing to Unity Catalog and add a heartbeat. Resist the urge to add caching, OLTP, or stateful streaming gymnastics until you&#8217;ve measured a real problem.</p><p>The teams that ship reliable agents they do it boringly. They benchmark before they scale. They write down their latency budget. They abstract their data access. They trace the things that matter.</p><p>The teams that ship unreliable agents skip ahead to the interesting parts and find out, in production, that the boring parts were the load-bearing parts.</p><p>You don&#8217;t need to pick the optimal architecture on day one. You need to pick one you can swap pieces of without rewriting everything. Get the three decisions above right and you will thank yourself later.</p><h2>FAQ</h2><h3><strong>1. What is the best default serving option for a new production agent?</strong></h3><p>Start with <strong>SQL Warehouse</strong>. It is the fastest path to production, requires the least new infrastructure, and is often sufficient once query patterns and table layout are optimized.</p><h3><strong>2. When should I add in-session caching?</strong></h3><p>Add it when a single agent session repeatedly queries the <strong>same underlying data</strong>. If sessions only issue one or two reads, caching usually adds complexity without much benefit.</p><h3><strong>3. When does an OLTP serving layer make sense?</strong></h3><p>Use it when you need <strong>very fast point lookups under high concurrency</strong> and have hard latency targets that the warehouse path cannot meet even after optimization.</p><h3><strong>4. Why should every tool call the same </strong><code>execute_sql(...)</code><strong> function?</strong></h3><p>A shared execution layer gives you <strong>one place</strong> to manage routing, retries, caching, parameterization, observability, and safety policies. It also makes future architecture changes much easier.</p><h3><strong>5. What is the biggest security mistake in agent data access?</strong></h3><p>Letting the model choose sensitive identifiers such as <strong>tenant or customer IDs</strong>. Those should be bound into the tool outside the model&#8217;s control to reduce isolation and prompt-injection risk.</p><h3><strong>6. What should I instrument first?</strong></h3><p>At minimum, trace <strong>input validation, tool dispatch, SQL execution, and model calls</strong>. Then add a synthetic heartbeat so you can measure actual end-to-end lag, not just component-level health.</p>]]></content:encoded></item><item><title><![CDATA[Multi-Genie Slack Bot: Production-Grade Conversational Analytics in Slack with Databricks Genie Spaces]]></title><description><![CDATA[Check out Databricks native Slack Integration before implementing a custom solution!]]></description><link>https://www.databricksters.com/p/multi-genie-slack-bot-production</link><guid isPermaLink="false">https://www.databricksters.com/p/multi-genie-slack-bot-production</guid><dc:creator><![CDATA[Ambarish]]></dc:creator><pubDate>Fri, 01 May 2026 15:02:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5ne9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5ne9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5ne9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!5ne9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!5ne9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!5ne9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5ne9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5ne9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!5ne9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!5ne9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!5ne9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05a5f66a-7283-47b9-b2b7-279a26945862_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p><em>Check out Databricks native Slack Integration before implementing a custom solution! </em></p><p><em>https://docs.databricks.com/aws/en/genie-one/genie-slack </em> </p><p>Connect multiple Databricks Genie spaces to Slack with per-user authentication, intelligent routing, semantic caching, and audit logging &#8212; all deployed as a Databricks App. A production-grade Slack bot that brings Databricks Genie directly into Slack, allowing users to ask natural-language questions across multiple data domains without leaving their workspace.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><em>Special Thanks</em></h2><p>I extend my sincere thanks to <em><strong>Sunil Patil</strong></em> (<a href="https://www.linkedin.com/in/pppsunil/">https://www.linkedin.com/in/pppsunil/</a>) for his invaluable help during the brainstorming phase. His insights were instrumental in solving the complex challenge of managing multiple genies across multiple Slack channels. This project definitely levelled up thanks to his help!</p><h1>Features</h1><ul><li><p>Multi-Genie Support: Route questions to the right Genie space based on channel, thread, AI classification</p></li><li><p>Per-User Authentication: Use OAuth 2.0 User-to-Machine with PKCE so queries run under each user&#8217;s Unity Catalog permissions</p></li><li><p>Threaded Conversations: Maintain Genie context across Slack threads</p></li><li><p>Semantic Cache: Reuse answers for similar questions using Databricks Vector Search</p></li><li><p>Delta-Aware Cache Invalidation: Automatically invalidate cached answers when underlying Delta tables change</p></li><li><p>Audit Logging: Track user activity, cache hits, latency, errors, and Genie usage in Delta tables</p></li><li><p>Reliable Genie Calls: Handle rate limits with retry, exponential backoff, and adaptive polling</p></li><li><p>Native Deployment: Run the entire application as a Databricks App with no external infrastructure</p></li></ul><h1>Architecture</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6d3_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6d3_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 424w, https://substackcdn.com/image/fetch/$s_!6d3_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 848w, https://substackcdn.com/image/fetch/$s_!6d3_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 1272w, https://substackcdn.com/image/fetch/$s_!6d3_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6d3_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6d3_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 424w, https://substackcdn.com/image/fetch/$s_!6d3_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 848w, https://substackcdn.com/image/fetch/$s_!6d3_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 1272w, https://substackcdn.com/image/fetch/$s_!6d3_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4984365d-ea4d-48d0-aff7-1f56863a02b5_1692x930.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The application runs as a Databricks App and uses two services:</p><ul><li><p>Slack Bolt in Socket Mode to listen for messages, mentions, button clicks, and threaded replies</p></li><li><p>Flask to handle OAuth callbacks and health checks</p></li></ul><p>When a user asks a question in Slack, the bot follows this flow:</p><p>1. Checks whether the Slack user has a valid Databricks OBO token</p><p>2. Routes the question to the correct Genie space</p><p>3. Looks for a semantically similar cached response</p><p>4. Validates cached answers against Delta table versions</p><p>5. Sends the question to Genie on cache miss</p><p>6. Formats the response in Slack with SQL, tables, and suggested follow-ups</p><p>7. Writes cache metadata and audit logs to Delta</p><h1>Prerequisites</h1><p>1. Databricks workspace</p><ul><li><p>Active Databricks workspace</p></li><li><p>One or more Genie spaces</p></li><li><p>SQL Warehouse</p></li><li><p>Unity Catalog permissions</p></li><li><p>Vector Search endpoint and index</p></li></ul><p>2. Slack App</p><ul><li><p>Slack workspace with admin access</p></li><li><p>Slack app with Socket Mode enabled</p></li><li><p>Bot token and app-level token</p></li></ul><p>3. OAuth App</p><ul><li><p>Databricks OAuth application configured for User-to-Machine authentication</p></li><li><p>Redirect URI pointing to the Databricks App callback endpoint</p></li></ul><p>4. Download the application code and make the necessary updates to the config values in app.yaml. Download all files from repo: <a href="https://github.com/adgitdemo/ad_databricks/tree/main/multi-genie-multi-slack-channel-app">https://github.com/adgitdemo/ad_databricks/tree/main/multi-genie-multi-slack-channel-app</a></p><h1>Setup Instructions</h1><h2>1. Slack App Setup</h2><ul><li><p>Create a Slack app and enable Socket Mode.</p></li><li><p>Under OAuth &amp; Permissions, add these bot token scopes:</p><ul><li><p>app_mentions:read</p></li><li><p>chat:write</p></li><li><p>im:history</p></li><li><p>im:read</p></li><li><p>im:write</p></li><li><p>channels:history</p></li><li><p>groups:history</p></li><li><p>users:read</p></li></ul></li><li><p>Subscribe to these bot events:</p><ul><li><p>app_mention</p></li><li><p>message.im</p></li><li><p>message.channels</p></li><li><p>message.groups</p></li></ul></li></ul><ul><li><p>Install the app to your workspace and save:</p><ul><li><p>Bot User OAuth Token</p></li><li><p>App-Level Token</p></li><li><p>Signing Secret</p></li></ul></li></ul><h2>2. Databricks OAuth Setup</h2><ul><li><p>Create a Databricks OAuth application for User-to-Machine authentication.</p></li><li><p>Configure the redirect URI to point to the Flask callback route exposed by the Databricks App.</p></li></ul><blockquote><p>The bot uses OAuth 2.0 with PKCE to securely link each Slack user to their Databricks identity. After authentication, the user&#8217;s pending question is automatically processed, so they do not need to re-type it.</p></blockquote><h2>3. Databricks Workspace Setup</h2><ul><li><p>Prepare the required Databricks resources:</p><ul><li><p>Genie spaces</p></li><li><p>SQL Warehouse</p></li><li><p>Cache Delta table</p></li><li><p>Audit Delta table</p></li><li><p>Vector Search endpoint</p></li><li><p>Vector Search index</p></li><li><p>Catalog and schema permissions</p></li></ul></li></ul><ul><li><p>Permission Grants Summary</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kjO1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kjO1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 424w, https://substackcdn.com/image/fetch/$s_!kjO1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 848w, https://substackcdn.com/image/fetch/$s_!kjO1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 1272w, https://substackcdn.com/image/fetch/$s_!kjO1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kjO1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png" width="714" height="289" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:289,&quot;width&quot;:714,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:42119,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/196065016?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kjO1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 424w, https://substackcdn.com/image/fetch/$s_!kjO1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 848w, https://substackcdn.com/image/fetch/$s_!kjO1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 1272w, https://substackcdn.com/image/fetch/$s_!kjO1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dcfacee-51fb-4db4-974d-ed5726c4ce5e_714x289.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>4. Configure the Application</h2><ul><li><p>Update the application configuration with:</p><ul><li><p>Slack bot token</p></li><li><p>Slack app token</p></li><li><p>Slack signing secret</p></li><li><p>Databricks workspace URL</p></li><li><p>OAuth client ID</p></li><li><p>OAuth redirect URI</p></li><li><p>Genie space mappings</p></li><li><p>Default Genie space alias</p></li><li><p>SQL Warehouse ID</p></li><li><p>Cache table name</p></li><li><p>Audit table name</p></li><li><p>Vector Search endpoint and index</p></li><li><p>Cache similarity threshold</p></li><li><p>Optional Service Principal fallback flag</p></li></ul></li></ul><ul><li><p>Example routing configuration:</p></li></ul><blockquote><p>{</p><p>  &#8220;trips-data&#8221;: {</p><p>    &#8220;space_id&#8221;: &#8220;your-trips-genie-space-id&#8221;,</p><p>    &#8220;description&#8221;: &#8220;Questions about trips, pickup locations, fares, and distance&#8221;</p><p>  },</p><p>  &#8220;finance&#8221;: {</p><p>    &#8220;space_id&#8221;: &#8220;your-finance-genie-space-id&#8221;,</p><p>    &#8220;description&#8221;: &#8220;Questions about revenue, spend, forecasts, and financial KPIs&#8221;</p><p>  }</p><p>}</p></blockquote><h1>Usage</h1><h2>In Slack</h2><h3>Direct Messages:</h3><p>Send a DM to your bot</p><p>Ask questions like: &#8220;Show me sales data for last month&#8221;</p><h3>Channel Mentions:</h3><p>Invite the bot to a channel: /invite @YourBotName</p><p>Mention the bot: @YourBotName what are the top 10 customers?</p><h3>Threaded Conversations:</h3><p>Continue asking questions in a thread</p><p>The bot maintains conversation context within threads</p><h1>Demo Time</h1><p>In the General Slack Channel, the bot will use the default Genie Space to answer questions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I51n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I51n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 424w, https://substackcdn.com/image/fetch/$s_!I51n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 848w, https://substackcdn.com/image/fetch/$s_!I51n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 1272w, https://substackcdn.com/image/fetch/$s_!I51n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I51n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png" width="1385" height="556" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:556,&quot;width&quot;:1385,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!I51n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 424w, https://substackcdn.com/image/fetch/$s_!I51n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 848w, https://substackcdn.com/image/fetch/$s_!I51n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 1272w, https://substackcdn.com/image/fetch/$s_!I51n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11ad4add-ebf6-4f46-af8a-06c7b1f8f004_1385x556.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>DM Thread, the bot will use the default Genie Space to answer questions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YUbT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YUbT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 424w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 848w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 1272w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YUbT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png" width="992" height="924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:992,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YUbT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 424w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 848w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 1272w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6a3b38-6010-4d03-9418-d59b735d71a6_992x924.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a dedicated Trip Slack Channel, the bot will use the Trip Genie Space to answer questions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rglc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rglc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 424w, https://substackcdn.com/image/fetch/$s_!rglc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 848w, https://substackcdn.com/image/fetch/$s_!rglc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 1272w, https://substackcdn.com/image/fetch/$s_!rglc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rglc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png" width="1386" height="858" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:858,&quot;width&quot;:1386,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rglc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 424w, https://substackcdn.com/image/fetch/$s_!rglc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 848w, https://substackcdn.com/image/fetch/$s_!rglc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 1272w, https://substackcdn.com/image/fetch/$s_!rglc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84e1c3ab-ea35-4c4f-9310-70cd7914ed0c_1386x858.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a dedicated weather Slack Channel, the bot will use the Weather Genie Space to answer questions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WUuT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WUuT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 424w, https://substackcdn.com/image/fetch/$s_!WUuT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 848w, https://substackcdn.com/image/fetch/$s_!WUuT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 1272w, https://substackcdn.com/image/fetch/$s_!WUuT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WUuT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png" width="1399" height="596" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6f02a528-ad26-451c-a262-616c7677f209_1399x596.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:596,&quot;width&quot;:1399,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WUuT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 424w, https://substackcdn.com/image/fetch/$s_!WUuT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 848w, https://substackcdn.com/image/fetch/$s_!WUuT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 1272w, https://substackcdn.com/image/fetch/$s_!WUuT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f02a528-ad26-451c-a262-616c7677f209_1399x596.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Stop Settling for Seconds: Building 5ms Ultra Low latency Pipelines on Databricks]]></title><description><![CDATA[A comprehensive demo/ framework to easily understand/tune/customize your mission critical real time mode workload using Databricks' Spark Realtime mode (RTM)]]></description><link>https://www.databricksters.com/p/stop-settling-for-seconds-building</link><guid isPermaLink="false">https://www.databricksters.com/p/stop-settling-for-seconds-building</guid><dc:creator><![CDATA[Emad R]]></dc:creator><pubDate>Tue, 14 Apr 2026 15:02:15 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/187423058/e1a5bc1b0c5bb39ad5e3b61a43fb1e4d.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p><strong>Introduction/ Preface</strong></p><p><a href="https://docs.databricks.com/aws/en/structured-streaming/real-time">Real-time streaming in the  Spark ecosystem</a> (RTM) is a great leap in Spark structured streaming, as it guarantees an end-to-end processing as low as 5 ms</p><p>This directly serves use cases where ultra-low latency is crucial for  operations, such as financial transactions and infrastructure monitoring, to name a few, which can directly translate to revenue loss or business image if not addressed in timely manners with Databricks&#8217; RTM streaming </p><p>In this blog, we will introduce a <a href="https://github.com/EmadRizk-db/kafka-spark-rtm-lakebase">simple demo notebook(s)</a> focusing on:</p><ul><li><p>Creating a simulated streaming dataset in a message bus system (Kafka) with control over</p><ul><li><p>Programmatically introducing duplicated records in the upstream</p></li><li><p>Queue partitions (As it impacts downstream processing/ patallelism)</p></li><li><p>Rows/Sec for writing into the message bus (Kafka is for demo purposes here but this can be any of the currently <a href="https://docs.databricks.com/aws/en/structured-streaming/real-time/reference#sources-and-sinks">supported sources by RTM</a></p></li></ul></li><li><p>Reading real-time messages from the message bus in Spark real-time streaming mode and writing down to Databricks&#8217; Lakebase serverless instance, including:</p><ul><li><p>A simple, direct write (no transformation) using <a href="https://docs.databricks.com/aws/en/structured-streaming/real-time">RTM</a></p></li><li><p>A simple stateful transformation (<a href="https://docs.databricks.com/aws/en/structured-streaming/real-time#deduplication">deduplication</a>)</p></li><li><p>Explicit tuning parameters for both scenarios/use cases in order to quickly evaluate your use case for RTM</p></li></ul></li></ul><p><strong>How to use this blog/code:</strong></p><ul><li><p>Real-time mode sizing framework</p><ul><li><p>You can change the upstream spec or</p></li><li><p>Downstream specs through the widgets that expose several tuning parameters </p></li></ul></li><li><p>Learning the ropes of a full pipeline, using foreach in a real-time streaming fashion to write down to a JDBC sink (Lakebase Postgres in our demo)</p></li><li><p>Clone the code from the <a href="https://github.com/EmadRizk-db/kafka-spark-rtm-lakebase">GitHub repo</a> to start your journey!</p></li></ul><p><strong>Compute resources used:</strong></p><ul><li><p>Any supported upstream system by Spark Realtime Mode</p><ul><li><p>In this case, we are using a Kafka topic that we can recreate with n partitions (default 8)</p></li></ul></li><li><p>Databricks classic cluster (no autoscaling)</p><ul><li><p>Driver: rd-fleet.xlarge (32 GB memory, 4 cores)</p></li><li><p>Workers: 8 rd-fleet.xlarge instances</p></li><li><p>DBR 17.3 LTS+</p></li></ul></li><li><p>Lakebase postgres instance with 2CU&#8217;s (<a href="https://docs.databricks.com/aws/en/oltp/projects/reverse-etl#how-it-works">Capacity Units</a>)</p></li></ul><p><strong>The flow Illustration:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wkeo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wkeo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 424w, https://substackcdn.com/image/fetch/$s_!wkeo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 848w, https://substackcdn.com/image/fetch/$s_!wkeo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 1272w, https://substackcdn.com/image/fetch/$s_!wkeo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wkeo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png" width="753" height="308" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/57cb6043-220a-4206-958e-18df9d775823_753x308.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:308,&quot;width&quot;:753,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:61528,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/187423058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wkeo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 424w, https://substackcdn.com/image/fetch/$s_!wkeo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 848w, https://substackcdn.com/image/fetch/$s_!wkeo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 1272w, https://substackcdn.com/image/fetch/$s_!wkeo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57cb6043-220a-4206-958e-18df9d775823_753x308.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Important parameters:</strong></p><p>In this framework, we included different parameters to control </p><p>Records generation (Upstream): </p><ul><li><p>Duplication of the same record (including the key) across different timestamps, to simulate a duplication happening upstream</p></li><li><p>Percentage of duplicate records generated</p></li><li><p>Partitions to be created (this will control the slots needed downstream when Spark reads from Kafka upstream in real-time fashion)</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!41pW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!41pW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 424w, https://substackcdn.com/image/fetch/$s_!41pW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 848w, https://substackcdn.com/image/fetch/$s_!41pW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 1272w, https://substackcdn.com/image/fetch/$s_!41pW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!41pW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png" width="1456" height="175" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:175,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46105,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/187423058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!41pW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 424w, https://substackcdn.com/image/fetch/$s_!41pW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 848w, https://substackcdn.com/image/fetch/$s_!41pW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 1272w, https://substackcdn.com/image/fetch/$s_!41pW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9004ab6-2951-4dad-a6a5-2c2eaa112332_1760x212.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Records persistence (Downstream):</p><ul><li><p>Dedup flag in order to decide a stateful or stateless transformation</p></li><li><p>A batching mechanism to increase throughput</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s4fJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s4fJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 424w, https://substackcdn.com/image/fetch/$s_!s4fJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 848w, https://substackcdn.com/image/fetch/$s_!s4fJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 1272w, https://substackcdn.com/image/fetch/$s_!s4fJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s4fJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png" width="1308" height="174" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:174,&quot;width&quot;:1308,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33371,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/187423058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s4fJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 424w, https://substackcdn.com/image/fetch/$s_!s4fJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 848w, https://substackcdn.com/image/fetch/$s_!s4fJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 1272w, https://substackcdn.com/image/fetch/$s_!s4fJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F318ece2a-99bd-43e3-ae80-2a3ae5235a2f_1308x174.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p><strong>Key Code Snippets to use/reuse:</strong></p><p>I think the major part that I&#8217;d like to expand on is two things, actually:</p><ul><li><p>defining a Python JDBC-backed code to implement <a href="https://docs.databricks.com/aws/en/structured-streaming/real-time/examples#write-to-postgresql-using-foreachsink">a Foreachwriter</a></p><p>The structure of this implementation requires at least open, process, and close implementations. Additionally, I added a buffer implementation that is time or row counts based in order to control the throughput<br>An implementation skeleton in Python would be as follows (full code available on <a href="https://github.com/EmadRizk-db/kafka-spark-rtm-lakebase">GitHub</a>)<br></p><pre><code>class PgForeachWriter:
    def open(self, partition_id, epoch_id):
        try:
            self.conn = psycopg2.connect(**conn_kwargs)
            self.conn.autocommit = False
            self.cursor = self.conn.cursor()
            
            # ...

            # Buffering state (only used in buffered mode)
            if use_buffered_mode:
                self.buffer = []
                self.last_flush_ts = time.time()
            
            return True
        
        except Exception as e:
            # ...
            return False

    def _flush_if_needed(self, force=False):
        ...

    def process(self, row):
        ...

    def close(self, error):
        ...


def make_pg_buffered_writer(
    jdbc_url,
    jdbc_user,
    jdbc_password,
    jdbc_driver,
    jdbc_jar_path,
    table_name,
    max_batch_size=100,  # flush when &gt;= this many rows (0 = simple mode)
    flush_secs=2.0,      # flush if last flush older than this (0 = simple mode)
):</code></pre></li><li><p>After defining the writer class, all you need is to use it in your writeStream with real-time mode, as shown below</p><pre><code>jdbc_writer = make_pg_buffered_writer(
    jdbc_url=JDBC_URL,
    jdbc_user=JDBC_USER,
    jdbc_password=JDBC_PASSWORD,
    jdbc_driver=JDBC_DRIVER,
    table_name=TABLE_NAME,
    max_batch_size=max_batch_size_param,
    flush_secs=flush_secs_param
)

query = (
    df_for_write
    .writeStream
    .foreach(jdbc_writer)
    .outputMode("update")
    .queryName("jdbc_sink_writer")
    .trigger(realTime="5 minutes")
    .option("checkpointLocation", checkpoint_path)
    .start()
)</code></pre></li></ul><p><strong>Performance measures:</strong></p><p>I ran many scenarios in simulation and consistently sustained around 10k writes/sec over several minutes, with minimal to no delays and no backlog, as shown below. This aligns with a production rate of around 10K upstream writes, which are read in real time by Spark's streaming and written to Lakebase. I was also able to achieve a similar throughput for stateful (deduplication) transformation with no drops </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wR3Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wR3Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 424w, https://substackcdn.com/image/fetch/$s_!wR3Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 848w, https://substackcdn.com/image/fetch/$s_!wR3Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 1272w, https://substackcdn.com/image/fetch/$s_!wR3Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wR3Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png" width="784" height="362" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2563a30-459b-471e-b135-1bacdb8e93de_784x362.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:362,&quot;width&quot;:784,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27195,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/187423058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wR3Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 424w, https://substackcdn.com/image/fetch/$s_!wR3Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 848w, https://substackcdn.com/image/fetch/$s_!wR3Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 1272w, https://substackcdn.com/image/fetch/$s_!wR3Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2563a30-459b-471e-b135-1bacdb8e93de_784x362.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ep-p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ep-p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 424w, https://substackcdn.com/image/fetch/$s_!ep-p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 848w, https://substackcdn.com/image/fetch/$s_!ep-p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 1272w, https://substackcdn.com/image/fetch/$s_!ep-p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ep-p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png" width="762" height="400" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:762,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43186,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/187423058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ep-p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 424w, https://substackcdn.com/image/fetch/$s_!ep-p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 848w, https://substackcdn.com/image/fetch/$s_!ep-p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 1272w, https://substackcdn.com/image/fetch/$s_!ep-p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e509fd-e80a-4111-8153-dad3d1bd51ea_762x400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!odWR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!odWR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 424w, https://substackcdn.com/image/fetch/$s_!odWR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 848w, https://substackcdn.com/image/fetch/$s_!odWR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 1272w, https://substackcdn.com/image/fetch/$s_!odWR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!odWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png" width="697" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/edf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:697,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29492,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/187423058?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!odWR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 424w, https://substackcdn.com/image/fetch/$s_!odWR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 848w, https://substackcdn.com/image/fetch/$s_!odWR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 1272w, https://substackcdn.com/image/fetch/$s_!odWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf2db74-f7cb-4e17-9c48-72d2c4e351c2_697x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p><strong>Key considerations:</strong></p><ul><li><p>RTM mode will handle any recs/sec rate when sized right</p></li><li><p>Lakebase will handle writes/sec in the 10&#8217;s K range per CU out of the box</p></li><li><p>That said, there are tuning parameters and things to consider:</p><ul><li><p>To enhance the write throughput, and depending on the use case you can:</p><ul><li><p>Consider <a href="https://docs.databricks.com/aws/en/oltp/projects/about">Lakebase Autoscaling</a></p></li><li><p>Consider batching the writes to downstream (this is generally best practice) to benefit from Postgres batch DML API&#8217;s </p></li><li><p>Batching can be time or number-of-rows-based (example provided in the repo)</p></li></ul></li><li><p>To enhance the RTM read in general:</p><ul><li><p>You can tune your windowing range for stateful transformation</p></li><li><p>Tune the  <a href="https://docs.databricks.com/aws/en/structured-streaming/triggers#trigger-modes-overview">realTime value (default 5 minutes)</a>; this parameter is used for checkpoint commit </p></li><li><p>Increase the number of upstream partitions</p></li></ul></li></ul></li></ul><p><strong>The sweet spot:</strong></p><ul><li><p>For the ideal optimum solution:</p><ul><li><p>Consider right-sizing of:</p><ul><li><p>upstream source and its partitions, as it will control parallelism downstream</p></li><li><p>Spark cluster size makes sure there are enough slots to schedule all stages at once </p></li><li><p>Downstream sizing (in our case, it is Lakebase) and consider autoscaling, more CU&#8217;s = More throughput</p></li></ul></li></ul></li></ul><p><strong>Conclusion:</strong></p><p>While the purpose of this exercise is to demonstrate a full pipeline using Lakebase PostgreSQL as the downstream, and is not benchmarking specifically </p><p>The E2E latency in test is P95 356 MS with a P50 of 225 MS</p><p>These figures are out of the box with no tuning of any sorts, it can be improved further.</p><p>Also, with 8 Kafka topic partitions and an adequate Databricks cluster configuration, there is ~0 lag in real-time streaming read from Kafka and  downstream write to Lakebase</p><p>That said, please note that your mileage will vary! In essence, that:</p><ul><li><p>A clear understanding of records production upstream is key</p></li><li><p>A correct sizing of the Spark cluster and the Downstream OLTP (Lakebase Postgres in our setup) is crucial for throughput</p></li><li><p>There is a trade-off between write capacity and the downstream cluster capacity units</p><ul><li><p>Time or number of rows is a good tuning parameter in such a case Or</p></li><li><p>Autoscaling downstream to scale to more throughput requirements should be considered</p></li></ul></li></ul><p></p><h2>&#128587; Frequently Asked Questions</h2><div><hr></div><p><strong>What is the main goal of this demo?</strong></p><p>It demonstrates how to build a low-latency streaming pipeline from Kafka through Spark Real-Time Mode into Lakebase, while exposing the main tuning knobs that affect throughput and lag.</p><div><hr></div><p><strong>What variables can I change in the simulation?</strong></p><p>You can adjust ingest rate, Kafka partition count, duplicate-record percentage, deduplication behavior, and downstream batching settings.</p><div><hr></div><p><strong>Why does Kafka partition count matter?</strong></p><p>Partition count affects how much parallelism Spark can use when reading the stream. Too few partitions can limit throughput even if the cluster has spare capacity.</p><div><hr></div><p><strong>When should I use buffered writes instead of direct writes?</strong></p><p>Buffered writes are usually better when downstream write throughput is the bottleneck. They reduce per-row overhead and improve efficiency for OLTP-style sinks.</p><div><hr></div><p><strong>Can stateful deduplication still perform well in Real-Time Mode?</strong></p><p>Yes &#8212; based on our test results, the pipeline achieved similar throughput with deduplication enabled, provided the job was sized appropriately.</p><div><hr></div><p><strong>What should I tune first in production?</strong></p><p>Start with upstream partitions, Spark cluster sizing, downstream write capacity, and batching thresholds. Then refine checkpoint cadence and stateful window settings.</p>]]></content:encoded></item><item><title><![CDATA[Migrating Existing Dashboards to Databricks AI/BI, Part 3: User Filters and Row-Level Security with Unity Catalog]]></title><description><![CDATA[How to implement user-based filtering and row-level security using dynamic views, row filters, column masks, and ABAC]]></description><link>https://www.databricksters.com/p/migrating-existing-dashboards-to-3e1</link><guid isPermaLink="false">https://www.databricksters.com/p/migrating-existing-dashboards-to-3e1</guid><dc:creator><![CDATA[Artem Chebotko]]></dc:creator><pubDate>Tue, 14 Apr 2026 15:01:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mMaQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mMaQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mMaQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!mMaQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!mMaQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!mMaQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mMaQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mMaQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!mMaQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!mMaQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!mMaQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3ac56e-a591-4cbe-8db1-eace395402a7_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As a Specialist Solutions Architect at Databricks, I often hear the same questions from customers who are migrating dashboards from legacy BI tools to Databricks AI/BI Dashboards:</p><ul><li><p><em>&#8220;What&#8217;s the Databricks equivalent of the context filters we use today?&#8221;</em></p></li><li><p><em>&#8220;Can we still do cascading filters where each dropdown only shows relevant values?&#8221;</em></p></li><li><p><em>&#8220;Do you support filter actions when I click on a bar or a point?&#8221;</em></p></li><li><p><em>&#8220;How do we do user-based filtering in AI/BI Dashboards?&#8221;</em></p></li></ul><p>In the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">first blog post in this series</a>, I focused on the first two questions and showed how to recreate:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>context filters using parameters in dataset SQL, and</p></li><li><p>&#8220;<em>Only Relevant Values</em>&#8221; filters using field filters and query-based parameters.</p></li></ul><p>In the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to-482">second post</a>, I focused on cross-filtering and drill-through interactions.</p><p>This third post tackles the remaining question: &#8220;<em>How do we do user-based filtering in AI/BI Dashboards?</em>&#8221;</p><p>In Databricks, these controls live in <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/">Unity Catalog</a>, not in the dashboard itself. AI/BI Dashboards query governed tables and views, and Unity Catalog enforces fine-grained access control before the data ever reaches the dashboard. I&#8217;ll walk through how to:</p><ul><li><p>Implement user-based filtering and RLS with <a href="https://docs.databricks.com/aws/en/views/dynamic">dynamic views</a> that use <code>current_user()</code> and <code>is_account_group_member()</code>.</p></li><li><p>Apply RLS directly on tables using <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">row filters</a> and protect sensitive fields with <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">column masks</a>.</p></li><li><p>Scale these patterns across many tables and columns using <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/abac/">ABAC tag policies and governed tags</a>.</p></li></ul><p>As in the previous posts, I&#8217;ll use the built-in <code>samples.tpch</code> dataset. I&#8217;ve also published the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a> so you can import it into your workspace, follow along as you read, and adapt these patterns to your own Unity Catalog data.</p><h3><strong>1. How User-Based Filtering Maps to Unity Catalog</strong></h3><p>Before we dive into SQL, it&#8217;s useful to clarify where these responsibilities live in Databricks.</p><p>In many BI tools, user-specific security is often implemented close to the dashboard or semantic layer:</p><ul><li><p>You define user- or group-based rules that map principals to specific regions, customers, or business units.</p></li><li><p>You may use identity-aware logic in filters or calculated fields.</p></li><li><p>You may maintain a security table that drives which slice of data each user can see.</p></li></ul><p>In Databricks, these controls live in <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/">Unity Catalog</a>, not in AI/BI Dashboards:</p><ul><li><p>Object privileges on catalogs, schemas, tables, and views control whether a user can query a given object at all.</p></li><li><p>Dynamic views, row filters, and column masks implement row-level security and masking at query time. They can inspect the current user and their groups and return different rows or values per user.</p></li><li><p>AI/BI Dashboards simply query those governed tables and views. They never bypass Unity Catalog: any row filters or masks you define apply to every query, regardless of whether it comes from a notebook, Databricks SQL, or an AI/BI dashboard.</p></li></ul><p>The result is conceptually similar to user-based filtering in traditional BI tools, but with one important shift: The security rules live with the data, not with a particular dashboard.</p><p>That&#8217;s especially important when:</p><ul><li><p>The same Unity Catalog tables power multiple AI/BI dashboards and external BI tools, and</p></li><li><p>You embed AI/BI Dashboards into applications where thousands of users see the same dashboard definition, but each must see a different subset of data.</p></li></ul><p>In the rest of this post, we&#8217;ll build up from that idea:</p><ol><li><p>Use a <a href="https://docs.databricks.com/aws/en/views/dynamic">dynamic view</a> and a permission table to enforce RLS on a TPCH Sales dataset.</p></li><li><p>Show how to do similar things directly on tables with <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">row filters and column masks</a>.</p></li><li><p>Discuss how to scale those patterns across many tables and columns using <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/abac/">ABAC tag policies and governed tags</a>.</p></li></ol><h3><strong>2. Building blocks: dynamic views, row filters, and column masks</strong></h3><p>To implement user-based filtering in Databricks, you really only need three Unity Catalog primitives: dynamic views, row filters, and column masks.</p><p>They all rely on the same core idea: At query time, Unity Catalog can look at who is running the query (and which groups they&#8217;re in), and then decide which rows and values to return.</p><h4><strong>2.1 Identity functions</strong></h4><p>The main functions you&#8217;ll use in policies are:</p><ul><li><p><code>current_user()</code><br>Returns the current user&#8217;s identity (usually their email).</p></li><li><p><code>is_account_group_member(&#8217;&lt;group_name&gt;&#8217;)</code><br>Returns <code>TRUE</code> if the current user is a member of an account-level group.</p></li></ul><p>You can call these functions from views and from SQL UDFs used by row filters and column masks.</p><h4><strong>2.2 Dynamic views</strong></h4><p>A <a href="https://docs.databricks.com/aws/en/views/dynamic">dynamic view</a> is just a normal SQL view whose logic depends on the current user or their groups.</p><p>You can:</p><ul><li><p>Filter rows based on <code>current_user()</code> or group membership.</p></li><li><p>Mask or null out columns for certain users.</p></li><li><p>Join to a separate permission table that maps users/groups to allowed regions, customers, etc.</p></li></ul><p>Any AI/BI dataset that selects from a dynamic view automatically inherits its logic. You don&#8217;t need to add any special configuration in AI/BI itself.</p><p>We&#8217;ll use a dynamic view for our first <em>TPCH Sales</em> RLS example.</p><h4><strong>2.3 Row filters</strong></h4><p>A <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">row filter</a> attaches RLS logic directly to a table, instead of wrapping the table in a view:</p><ul><li><p>You define a <a href="https://docs.databricks.com/aws/en/udf/unity-catalog">SQL UDF</a> that takes one or more columns as input and returns <code>BOOLEAN</code>.</p></li><li><p>You attach it to a table with <code>ALTER TABLE ... SET ROW FILTER ... ON (column[, ...])</code>.</p></li></ul><p>The row filter runs for every query and can call <code>current_user()</code> and <code>is_account_group_member()</code> internally. This is handy when you want:</p><ul><li><p>A stable table name (no extra view layer), or</p></li><li><p>A single table that&#8217;s consumed by many tools, all of which should respect the same RLS.</p></li></ul><p>We&#8217;ll look at both group-based and <code>current_user()</code> + permission-table examples later in the post.</p><h4><strong>2.4 Column masks</strong></h4><p>A <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">column mask</a> is similar, but operates at the column level:</p><ul><li><p>You define a <a href="https://docs.databricks.com/aws/en/udf/unity-catalog">SQL UDF</a> that returns a &#8220;masked&#8221; value.</p></li><li><p>You attach it with <code>ALTER TABLE ... ALTER COLUMN ... SET MASK ...</code>.</p></li></ul><p>This lets you:</p><ul><li><p>Show full values (for example, email or salary) only to certain groups.</p></li><li><p>Show partially masked or null values to everyone else.</p></li></ul><p>Think of it as the Unity Catalog side of &#8220;row-level security + column-level masking&#8221; that you might combine in legacy BI tools using data source filters and calculated fields.</p><p>Next, we&#8217;ll put these pieces together in a concrete example: enforcing region-based RLS on a <em>TPCH Sales</em> dataset using a combination of a base view, a permission table, and a dynamic view.</p><h3><strong>3. Implementing RLS with a dynamic view</strong></h3><p>Let&#8217;s start with a concrete scenario:</p><ul><li><p>You have a <em>TPCH Sales</em> dashboard shared across multiple sales teams.</p></li><li><p><em>NA Sales Managers</em> should see only <em>AMERICA</em>.</p></li><li><p><em>EMEA Sales Managers</em> should see only <em>EUROPE</em>.</p></li><li><p><em>APAC Sales Managers</em> should see only <em>ASIA</em>.</p></li><li><p>Individual users may have their own custom regions.</p></li></ul><p>In many traditional BI tools, you&#8217;d typically solve this with a user filter or data source filter that maps groups to <em>Regions</em>, and a security table to keep that mapping up to date.</p><p>In Databricks, we&#8217;ll use the same logical pattern &#8211; but move it into Unity Catalog:</p><ol><li><p>Create a base view for <em>TPCH Sales</em> in a demo schema.</p></li><li><p>Create a permission table that maps principals (groups or users) to <em>Regions</em>.</p></li><li><p>Create a dynamic view that joins the base view to the permission table and applies row-level security based on <code>current_user()</code> and <code>is_account_group_member()</code>.</p></li><li><p>Create an AI/BI dataset on top of that dynamic view and build a simple table visualization.</p></li></ol><p>Throughout this section, we&#8217;ll:</p><ul><li><p>Read from <code>samples.tpch</code> (which everyone has).</p></li><li><p>Create objects in <code>main.demo_tpch</code> (you can substitute another catalog/schema if needed).</p></li></ul><p>All of the <code>CREATE</code> / <code>INSERT</code> statements in this section should be run in a notebook or SQL editor, not inside an AI/BI dataset. In AI/BI, you&#8217;ll just <code>SELECT</code> from the resulting view.</p><h4><strong>3.1 </strong><em><strong>TPCH Sales</strong></em><strong> base view</strong></h4><p>First, set up a simple demo schema and define a reusable base view for <em>TPCH Sales</em>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Use the default UC catalog and create a demo schema
USE CATALOG main;
CREATE SCHEMA IF NOT EXISTS demo_tpch;

-- Base TPCH Sales view, reading from samples.tpch
CREATE OR REPLACE VIEW main.demo_tpch.tpch_sales_base AS
SELECT
  r.r_name              AS region,
  n.n_name              AS nation,
  c.c_custkey           AS customer_id,
  c.c_name              AS customer_name,
  o.o_orderkey          AS order_id,
  o.o_orderdate         AS order_date,
  l.l_extendedprice * (1 - l.l_discount) AS revenue
FROM samples.tpch.region   AS r
JOIN samples.tpch.nation   AS n ON n.n_regionkey = r.r_regionkey
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
JOIN samples.tpch.orders   AS o ON o.o_custkey   = c.c_custkey
JOIN samples.tpch.lineitem AS l ON l.l_orderkey  = o.o_orderkey;</code></pre></div><h4><strong>3.2 Region access permission table</strong></h4><p>Next, create a permission table that describes who is allowed to see which <em>Region</em>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Optional: you can keep security tables in the same schema
-- or create a separate one, e.g. main.demo_security
CREATE SCHEMA IF NOT EXISTS main.demo_tpch;

CREATE TABLE IF NOT EXISTS main.demo_tpch.tpch_region_access (
  principal_type STRING,   -- 'group' or 'user'
  principal      STRING,   -- group name or user email
  region         STRING    -- must match tpch_sales_base.region
);

INSERT INTO main.demo_tpch.tpch_region_access VALUES
  ('group', 'NA Sales Managers',        'AMERICA'),
  ('group', 'EMEA Sales Managers',      'EUROPE'),
  ('group', 'APAC Sales Managers',      'ASIA'),
  ('user',  'some.user@databricks.com', 'ASIA'),
  ('user',   current_user(),            'AFRICA');</code></pre></div><p>What this does:</p><ul><li><p>The first three rows grant access based on account-level groups</p></li><li><p>The fourth row grants access to <em>ASIA</em> to a specific user, even if they are not in one of those groups.</p></li><li><p>The fifth row uses <code>current_user()</code> to grant you, the person running this SQL, access to <em>AFRICA</em>. When you execute the <code>INSERT</code>, Unity Catalog evaluates <code>current_user()</code> to your own email.</p></li></ul><p>If you&#8217;re not in any of the <em>NA/EMEA/APAC Sales Managers</em> groups and you&#8217;re not <em>some.user@databricks.com</em>, the only applicable rule for you will be the one that says you can see <em>AFRICA</em>. We&#8217;ll see the effect of that in a moment when we query through the dynamic view.</p><h4><strong>3.3 Dynamic view with region-level RLS</strong></h4><p>Now create a <a href="https://docs.databricks.com/aws/en/views/dynamic">dynamic view</a> that applies row-level security by joining the base <em>TPCH Sales</em> view to the permission table and checking the current user&#8217;s identity and groups:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE OR REPLACE VIEW main.demo_tpch.tpch_sales_rls AS
SELECT s.*
FROM   main.demo_tpch.tpch_sales_base AS s
WHERE EXISTS (
  SELECT 1
  FROM   main.demo_tpch.tpch_region_access a
  WHERE  a.region = s.region
    AND (
      (a.principal_type = 'group'
       AND is_account_group_member(a.principal))
      OR
      (a.principal_type = 'user'
       AND a.principal = current_user())
    )
);</code></pre></div><p>This view enforces RLS as follows. For each row in <code>tpch_sales_base</code>, it looks for a matching rule in <code>tpch_region_access</code> based on region and either group or user principle. If no matching rule exists for the current user and that region, the row is filtered out.</p><p>You can verify this by running:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT DISTINCT region
FROM main.demo_tpch.tpch_sales_rls;</code></pre></div><p>If you are only granted access via the <code>(&#8217;user&#8217;, current_user(), &#8216;AFRICA&#8217;)</code> row, you should see: <em>AFRICA</em>.</p><p>From now on:</p><ul><li><p>Any query against <code>main.demo_tpch.tpch_sales_rls</code> returns only the regions granted to the current user.</p></li><li><p>This applies uniformly whether the query comes from a notebook, Databricks SQL, or an AI/BI Dashboard.</p></li><li><p>You can add or revoke access simply by inserting or deleting rows in <code>main.demo_tpch.tpch_region_access</code> &#8211; you don&#8217;t need to change the view logic.</p></li></ul><h4><strong>3.4 Using the dynamic view in an AI/BI dataset</strong></h4><p>With <code>main.demo_tpch.tpch_sales_rls</code> in place, using it in AI/BI Dashboards is straightforward. You don&#8217;t need to re-implement any RLS logic in AI/BI &#8211; the dataset just selects from the governed view.</p><p>Create the <em>TPCH Sales (RLS Dynamic View)</em> dataset:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT
  region,
  nation,
  customer_id,
  customer_name,
  order_id,
  order_date,
  revenue
FROM main.demo_tpch.tpch_sales_rls;</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dlAq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dlAq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 424w, https://substackcdn.com/image/fetch/$s_!dlAq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 848w, https://substackcdn.com/image/fetch/$s_!dlAq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 1272w, https://substackcdn.com/image/fetch/$s_!dlAq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dlAq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png" width="1456" height="681" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a92013e6-065f-4405-8564-a23013c9a17c_1600x748.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:681,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dlAq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 424w, https://substackcdn.com/image/fetch/$s_!dlAq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 848w, https://substackcdn.com/image/fetch/$s_!dlAq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 1272w, https://substackcdn.com/image/fetch/$s_!dlAq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92013e6-065f-4405-8564-a23013c9a17c_1600x748.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Build a simple dashboard page to visualize the dataset. In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, I created a <em>RLS with dynamic view</em> page based on this dataset:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!atyN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!atyN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 424w, https://substackcdn.com/image/fetch/$s_!atyN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 848w, https://substackcdn.com/image/fetch/$s_!atyN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 1272w, https://substackcdn.com/image/fetch/$s_!atyN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!atyN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png" width="1456" height="431" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:431,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!atyN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 424w, https://substackcdn.com/image/fetch/$s_!atyN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 848w, https://substackcdn.com/image/fetch/$s_!atyN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 1272w, https://substackcdn.com/image/fetch/$s_!atyN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F108ce78d-3404-44ae-9cc7-f7e57a4651ab_1600x474.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When you view this page, if your only grant is the row we inserted with <code>(&#8217;user&#8217;, current_user(), &#8216;AFRICA&#8217;)</code>, the table will show only rows where the region is <em>AFRICA</em>.</p><p>The security logic lives in Unity Catalog (dynamic view + permission table). The dashboard just selects from <code>tpch_sales_rls</code> and automatically respects row-level security for each viewer.</p><h3><strong>4. Implementing RLS on tables with row filters and column masks</strong></h3><p>In the previous section, we implemented row-level security for <em>TPCH Sales</em> using a dynamic view and a permission table. That pattern works well when you want a named, shareable view to point AI/BI datasets at.</p><p>Unity Catalog also lets you attach RLS and masking logic directly to tables using:</p><ul><li><p><a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">Row filters</a> &#8211; control which rows a user can access in a table.</p></li><li><p><a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">Column masks</a> &#8211; control what values they see in specific columns.</p></li></ul><p>These policies are evaluated in Unity Catalog at query time and apply to all compute &#8211; SQL warehouses, notebooks, and AI/BI Dashboards. Unlike dynamic views, they keep the table name unchanged, which can be important when the same table is shared across many tools.</p><p>In this section, we&#8217;ll:</p><ol><li><p>Create a <em>TPCH Sales</em> table in <code>main.demo_tpch</code> for row filters and masks.</p></li><li><p>Attach a group-based row filter that restricts Regions.</p></li><li><p>Attach a user-based row filter that uses <code>current_user()</code> and a permission table.</p></li><li><p>Add a column mask to protect a sensitive column.</p></li></ol><p>Any AI/BI dataset that selects from this table will automatically respect these policies. You don&#8217;t need to configure anything special in AI/BI.</p><p>All of the statements below should be run in a notebook or SQL editor. AI/BI Dashboards just query the resulting table.</p><h4><strong>4.1 TPCH Sales table for filters and masks</strong></h4><p>Row filters and column masks only apply to tables (and a few other relation types), not to views. To keep things simple, we&#8217;ll materialize the tpch_sales_base view from Section 3 into a Delta table that we can attach policies to:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Use the same demo catalog and schema as before
USE CATALOG main;
USE SCHEMA demo_tpch;

-- Create a physical table from the base view for row filters and masks
CREATE OR REPLACE TABLE main.demo_tpch.tpch_sales_table AS
SELECT *
FROM main.demo_tpch.tpch_sales_base;</code></pre></div><p>From now on, we&#8217;ll attach row filters and masks to <code>main.demo_tpch.tpch_sales_table</code>.<br>If you point an AI/BI dataset at this table instead of the dynamic view, the behavior will be controlled by these table-level policies.</p><h4><strong>4.2 Group-based row filter on </strong><em><strong>Region</strong></em></h4><p>First, let&#8217;s attach a row filter that enforces the same <em>Region</em> rules we used in the dynamic view, but purely based on account-level groups:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Row filter function that decides which regions each group can see
CREATE OR REPLACE FUNCTION main.demo_tpch.tpch_region_filter(p_region STRING)
RETURNS BOOLEAN
RETURN
  CASE
    WHEN is_account_group_member('NA Sales Managers')   THEN p_region = 'AMERICA'
    WHEN is_account_group_member('EMEA Sales Managers') THEN p_region = 'EUROPE'
    WHEN is_account_group_member('APAC Sales Managers') THEN p_region = 'ASIA'
    ELSE FALSE
  END;</code></pre></div><p>Attach it to the table:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE main.demo_tpch.tpch_sales_table
  SET ROW FILTER main.demo_tpch.tpch_region_filter ON (region);</code></pre></div><p>Effect:</p><ul><li><p>Whenever anyone queries <code>main.demo_tpch.tpch_sales_table</code>, Unity Catalog evaluates <code>tpch_region_filter(region)</code> for each row.</p></li><li><p>If the user is in NA Sales Managers, only rows where the region is AMERICA are returned. If they&#8217;re in <em>EMEA Sales Managers</em>, they only see <em>EUROPE</em>; <em>APAC Sales Managers</em> see <em>ASIA</em>.</p></li><li><p>Users not in any of these groups see no rows from this table.</p></li></ul><p>If you point a dataset at:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT
  region,
  nation,
  customer_id,
  customer_name,
  order_id,
  order_date,
  revenue
FROM main.demo_tpch.tpch_sales_table;</code></pre></div><p>Any visualizations based on such a dataset will now respect the group-based <em>Region</em> logic without going through the dynamic view.</p><h4><strong>4.3 User-based row filter with </strong><code>current_user()</code><strong> and a permission table</strong></h4><p>Group-based rules are great for broad roles, but you may need finer control &#8211; different users seeing different subsets of customers, accounts, or regions.</p><p>We can reuse the same pattern as in Section 3 &#8211; <code>current_user()</code> + a permission table &#8211; but this time embed it in a row filter function instead of a dynamic view.</p><p>Example: restrict access by <code>customer_id</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Permission table mapping users to customers they can see
CREATE TABLE IF NOT EXISTS main.demo_tpch.customer_access (
  user_email  STRING,
  customer_id BIGINT
);

-- Example grants
INSERT INTO main.demo_tpch.customer_access VALUES
  ('some.user@databricks.com', 889),
  (current_user(),             1111);  -- Give yourself access</code></pre></div><p>Now create a row filter function that consults this table. To avoid ambiguous name resolution with the <code>customer_id</code> column on the table, we&#8217;ll use a parameter name <code>p_customer_id</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE OR REPLACE FUNCTION main.demo_tpch.tpch_customer_filter(p_customer_id BIGINT)
RETURNS BOOLEAN
RETURN EXISTS (
  SELECT 1
  FROM   main.demo_tpch.customer_access a
  WHERE  a.user_email  = current_user()
    AND  a.customer_id = p_customer_id
);</code></pre></div><p>Attach it to the same table. Because a table can have only one row filter, we&#8217;ll replace the <em>Region</em> filter from the previous subsection in this example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE main.demo_tpch.tpch_sales_table
  DROP ROW FILTER;

ALTER TABLE main.demo_tpch.tpch_sales_table
  SET ROW FILTER main.demo_tpch.tpch_customer_filter ON (customer_id);</code></pre></div><p>Effect:</p><ul><li><p>Whenever someone queries <code>main.demo_tpch.tpch_sales_table</code>, Unity Catalog evaluates <code>tpch_customer_filter(customer_id)</code> for each row.</p></li><li><p>For a given user, only rows whose <code>customer_id</code> appears in <code>main.demo_tpch.customer_access</code><br>for <code>current_user()</code> are returned.</p></li><li><p>In the sample data above, you will only see orders for customer <code>1111</code>, while <code>some.user@databricks.com</code> will see orders for customer <code>889</code>.</p></li></ul><p>You can verify this quickly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT DISTINCT customer_id
FROM main.demo_tpch.tpch_sales_table
ORDER BY customer_id
LIMIT 20;</code></pre></div><p>If your only mapping is <code>(current_user(), 1111)</code>, this query should return just <code>1111</code>.</p><p>To use this in AI/BI Dashboards, you can point a dataset directly at the table:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT
  region,
  nation,
  customer_id,
  customer_name,
  order_id,
  order_date,
  revenue
FROM main.demo_tpch.tpch_sales_table;</code></pre></div><p>In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, I created a <em>RLS with row filter</em> page based on this dataset:<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Pq42!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Pq42!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 424w, https://substackcdn.com/image/fetch/$s_!Pq42!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 848w, https://substackcdn.com/image/fetch/$s_!Pq42!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 1272w, https://substackcdn.com/image/fetch/$s_!Pq42!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pq42!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png" width="1456" height="441" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:441,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Pq42!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 424w, https://substackcdn.com/image/fetch/$s_!Pq42!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 848w, https://substackcdn.com/image/fetch/$s_!Pq42!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 1272w, https://substackcdn.com/image/fetch/$s_!Pq42!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2322b426-4b34-429a-b7bf-74cb5b9d9dbe_1600x485.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When you open it, the table and visuals only show data for customers you are allowed to see according to <code>customer_access</code>, without any RLS logic in the dashboard itself.</p><h4><strong>4.4 Bonus: masking sensitive columns with a column mask</strong></h4><p>Row filters decide which rows a user can see. Sometimes you also need to partially hide sensitive values within those rows &#8211; for example, masking customer names, emails or phone numbers for most users while leaving them fully visible for a small group.</p><p>Unity Catalog column masks handle this at the column level using the same pattern: a SQL UDF that can branch on <code>current_user()</code> or group membership.</p><p>Suppose we want:</p><ul><li><p>Users in a <em>PII Full Access</em> group to see full customer names.</p></li><li><p>Everyone else to see a partially masked version (for example, just the first few characters).</p></li></ul><p>First, define a masking function:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE OR REPLACE FUNCTION main.demo_tpch.mask_customer_name(name STRING)
RETURNS STRING
RETURN
  CASE
    WHEN is_account_group_member('PII Full Access') THEN name
    ELSE concat(substr(name, 1, 3), '***')
  END;</code></pre></div><p>Attach it as a mask on the table:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE main.demo_tpch.tpch_sales_table
  ALTER COLUMN customer_name
  SET MASK main.demo_tpch.mask_customer_name;</code></pre></div><p>Effect:</p><ul><li><p>Users in the <em>PII Full Access</em> group see the full <code>customer_name</code> value.</p></li><li><p>All other users see a masked version like <code>Cus***</code> instead of <code>Customer#000001111</code>.</p></li><li><p>The mask is enforced for every query against <code>tpch_sales_table</code> &#8211; notebooks, SQL editor, and AI/BI Dashboards &#8211; including exports.</p></li></ul><p>Here is what it looks like in the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9SDT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9SDT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 424w, https://substackcdn.com/image/fetch/$s_!9SDT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 848w, https://substackcdn.com/image/fetch/$s_!9SDT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 1272w, https://substackcdn.com/image/fetch/$s_!9SDT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9SDT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png" width="1456" height="519" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:519,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9SDT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 424w, https://substackcdn.com/image/fetch/$s_!9SDT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 848w, https://substackcdn.com/image/fetch/$s_!9SDT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 1272w, https://substackcdn.com/image/fetch/$s_!9SDT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e3b91a0-d851-4081-9a2a-aa550bf2a506_1600x570.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>5. Scaling RLS with ABAC and governed tags</strong></h3><p>Everything we&#8217;ve done so far (dynamic views, row filters, and column masks) is defined directly on individual Unity Catalog objects. That&#8217;s fine for a handful of tables, but it becomes hard to manage when you have dozens of catalogs, hundreds of schemas, and thousands of tables. This is exactly the problem that <a href="https://docs.databricks.com/gcp/en/data-governance/unity-catalog/abac/">attribute-based access control (ABAC)</a> with governed tags is designed to solve in Unity Catalog.</p><p>At a high level, ABAC adds three building blocks on top of the mechanisms we already used:</p><ul><li><p>Governed tags &#8211; account-level tags like <em>sensitivity</em>, <em>business_domain</em>, or <em>region_scope</em>, with a controlled set of allowed values. You attach these tags to catalogs, schemas, tables, or columns.</p></li><li><p>Policy UDFs &#8211; reusable SQL UDFs that implement row-filter or column-mask logic, similar to the functions we wrote earlier, but intended to be reused across many datasets.</p></li><li><p>ABAC policies &#8211; centrally managed policies that say &#8220;when a tagged object matches these conditions, apply this row filter or column mask for these principals.&#8221; Policies can be attached at the catalog, schema, or table level and inherit down the hierarchy.</p></li></ul><p>Databricks recommends using ABAC as the primary way to apply row filters and column masks at scale, and reserving table-by-table configuration for special cases. Conceptually, you can think of ABAC as &#8220;lifting&#8221; the patterns from Section 4 into a central policy layer:</p><ol><li><p>Tag the data once &#8211; apply governed tags to catalogs, schemas, tables, and columns that participate in RLS or masking.</p></li><li><p>Register reusable UDFs &#8211; define shared row-filter and mask functions in a governance schema (for example, <code>governance.region_filter()</code> and <code>governance.mask_customer_name()</code>).</p></li><li><p>Create ABAC policies &#8203;&#8203;&#8211; define policies that attach those UDFs to tagged objects based on tag conditions and target groups.</p></li><li><p>Let tags drive behavior &#8211; as new tables and columns are tagged, the appropriate row filters and masks are applied automatically by Unity Catalog.</p></li></ol><p>From an AI/BI Dashboards perspective, the experience is the same as in Sections 3 and 4: the dataset SQL stays simple, and the dashboard filters and visualizations work as usual. The difference is that the security logic is now centralized in ABAC policies and tags instead of being embedded directly into each table or view.</p><h3><strong>6. Summary and next steps</strong></h3><p>In many BI tools, user-based filtering and row-level security are often implemented close to the dashboard or semantic layer using user/group mappings, security tables, and identity-aware logic. In Databricks, the key shift is that these controls move into <a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/">Unity Catalog</a>, and AI/BI Dashboards simply query governed tables and views.</p><p>In this post, we walked through three main patterns:</p><ul><li><p><a href="https://docs.databricks.com/aws/en/views/dynamic">Dynamic views and permission tables</a> (Section 3)<strong><br></strong> We built <code>main.demo_tpch.tpch_sales_rls</code> on top of a base TPCH Sales view and a <code>tpch_region_access</code> table. The dynamic view uses <code>current_user()</code> and <code>is_account_group_member()</code> to return different Regions for different users and groups. AI/BI datasets that query this view automatically inherit the row-level security.</p></li><li><p><a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/">Table-level row filters and column masks</a> (Section 4)<strong><br></strong> We materialized TPCH Sales into <code>main.demo_tpch.tpch_sales_table</code> and attached a row filter that looks up allowed <code>customer_id</code> values in <code>customer_access</code> based on <code>current_user()</code>. We also added a column mask for <code>customer_name</code>, showing full names only to a privileged group. Any dataset that selects from this table sees the combined effect of RLS and masking without any extra logic in the dashboard.</p></li><li><p><a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/abac/">ABAC and governed tags</a> (Section 5)<strong><br></strong> We then zoomed out to show how ABAC can apply the same kinds of row filters and masks at scale, using governed tags, reusable policy UDFs, and central ABAC policies. Instead of configuring each table or view by hand, you tag data once and let policies attach the right filters and masks automatically.</p></li></ul><p>Across all three patterns, the core idea is the same: <strong>AI/BI Dashboards stay simple; Unity Catalog enforces who sees which rows and what values.</strong></p><p>The <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a> brings these ideas together in a concrete, runnable example. If you import it into your workspace and wire it up to the views and tables from this post, you can see exactly how the visuals behave for different users as Unity Catalog applies dynamic views, row filters, and column masks behind the scenes.</p><p>Combined with the first two posts in this series (<a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">Part 1</a> and <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to-482">Part 2</a>), you now have a practical set of patterns for implementing filtering, drill-through, and row-level security in Databricks AI/BI Dashboards on top of Unity Catalog.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The Kafka TTL Trap: Updating Spark Streaming Tables Without Data Loss]]></title><description><![CDATA[How to update streaming bronze tables in Spark after your source data has expired.]]></description><link>https://www.databricksters.com/p/beating-kafkas-clock-the-zero-data</link><guid isPermaLink="false">https://www.databricksters.com/p/beating-kafkas-clock-the-zero-data</guid><dc:creator><![CDATA[Neil Wilson]]></dc:creator><pubDate>Tue, 24 Mar 2026 15:01:25 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2ac95c65-630b-48c7-b643-405af3cbe2d8_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR: Updating Streaming Tables Without Data Loss</strong></p><ul><li><p><strong>The Problem:</strong> A &#8220;Full Refresh&#8221; on Kafka-fed pipelines can cause <strong>permanent data loss</strong> if older records have aged out of the topic (TTL).</p></li><li><p><strong>The Strategy:</strong> Use a <strong>&#8220;Backup-and-Rebase&#8221;</strong> workflow: archive existing data, identify the last processed offsets, and point the new pipeline to that exact starting position.</p></li><li><p><strong>The Execution:</strong> This guide demonstrates how to manually configure <code>startingOffsets</code> in Spark to bridge the gap between historical backups and new Kafka data.</p></li><li><p><strong>The Result:</strong> Seamless streaming table updates with zero data loss or record duplication.</p></li></ul><h2>The Constraint: Kafka TTL &amp; Streaming Table Immutability</h2><p>In Spark Declarative Pipelines (SDP), situations may arise when you need to alter a bronze Streaming Table that is being fed by Apache Kafka. This can pose a challenge as you cannot manually alter Streaming Tables via Alter Table commands. <br><br>This is further complicated by the fact that Kafka topics are configured with a finite retention period (Time-to-live or TTL), meaning older records eventually age out. Since a pipeline Full Refresh clears the target Delta Table, you cannot simply update your pipeline definition and full refresh from the source, as older records will be missing. The diagram below illustrates this situation. The Kafka cluster contains user-4 who has already been ingested, and users 5 and 6 who still need to be ingested into our Lakehouse. The older user records (1, 2, and 3) have aged out of the topic&#8217;s TTL.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LMhK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LMhK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 424w, https://substackcdn.com/image/fetch/$s_!LMhK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 848w, https://substackcdn.com/image/fetch/$s_!LMhK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 1272w, https://substackcdn.com/image/fetch/$s_!LMhK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LMhK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png" width="767" height="583" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:583,&quot;width&quot;:767,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32107,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LMhK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 424w, https://substackcdn.com/image/fetch/$s_!LMhK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 848w, https://substackcdn.com/image/fetch/$s_!LMhK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 1272w, https://substackcdn.com/image/fetch/$s_!LMhK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23461c4d-4ca6-4f67-ba66-caf45b929828_767x583.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As new records are constantly being appended to this topic, how can I update my pipeline and alter my result table without missing new records (users 5, 6, etc), dropping old records (users 1, 2, and 3), or duplicating records (user-4)? The following example has been fabricated to show the solution. The actual reason for implementing this will vary by use-case.</p><h2>Example: Ingesting JSON via Kafka</h2><p>Imagine a pipeline is ingesting JSON data from Kafka. This JSON data contains three high-level fields: name, country, and email. It also contains two nested fields &#8220;event&#8221; and &#8220;device&#8221; which contain information about what actions users are taking from which devices. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;bb338c7a-d588-4351-840e-76569893f3ce&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{&#8221;user&#8221;:&#8221;user-2&#8221;,&#8221;email&#8221;:&#8221;user-2@example.com&#8221;,
&#8220;country&#8221;:&#8221;CA&#8221;,
&#8220;device&#8221;:{&#8221;os&#8221;:&#8221;android&#8221;,&#8221;model&#8221;:&#8221;Pixel 7&#8221;,&#8221;geo&#8221;:{&#8221;lat&#8221;:39.98,&#8221;lon&#8221;:-82.98}},
&#8220;event&#8221;:{&#8221;name&#8221;:&#8221;demo&#8221;,&#8221;seq&#8221;:2,&#8221;ts&#8221;:&#8221;2026-02-17T14:40:26.189Z&#8221;}}</code></pre></div><p>This data is being written to a json_bronze table, and is storing the user, email, and country fields as String and the nested fields as Struct types. It also adds Kafka metadata fields.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;d6daedb6-3323-4952-9560-62e6c1225bf7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import dlt
from pyspark.sql.functions import current_timestamp, col, from_json, expr

SERVERS = "REDACTED"
TOPIC = &#8220;neil_struct_topic&#8221;

# Explicit JSON schema for predictable demos (no schemaLocationKey)
SCHEMA_DDL = &#8220;&#8221;&#8220;
  user STRING,
  email STRING,
  country STRING,
  device STRUCT&lt;os: STRING, model: STRING, geo: STRUCT&lt;lat: DOUBLE, lon: DOUBLE&gt;&gt;,
  event STRUCT&lt;name: STRING, seq: BIGINT, ts: STRING&gt;
&#8220;&#8221;&#8220;

@dlt.table(
    name=&#8221;json_bronze&#8221;,
    comment=&#8221;Raw Kafka payload with explicit JSON schema and rescued data&#8221;,
    table_properties={&#8221;quality&#8221;: &#8220;bronze&#8221;}
)
def json_bronze():
    df = (
        spark.readStream
            .format(&#8221;kafka&#8221;)
            .option(&#8221;kafka.bootstrap.servers&#8221;, SERVERS)
            .option(&#8221;kafka.security.protocol&#8221;, &#8220;SSL&#8221;)
            .option(&#8221;subscribe&#8221;, TOPIC)
            .option(&#8221;startingOffsets&#8221;, &#8220;earliest&#8221;)
            .load()
    )

    parsed = (
        df.selectExpr(&#8221;CAST(value AS STRING) AS json_str&#8221;, &#8220;topic&#8221;, &#8220;partition&#8221;, &#8220;offset&#8221;, &#8220;timestamp&#8221;)
          .select(
              from_json(
                  col(&#8221;json_str&#8221;),
                  SCHEMA_DDL,
                  options={&#8221;rescuedDataColumn&#8221;: &#8220;_rescued_data&#8221;}  # capture type mismatches/new fields
              ).alias(&#8221;data&#8221;),
              &#8220;topic&#8221;, &#8220;partition&#8221;, &#8220;offset&#8221;, &#8220;timestamp&#8221;
          )
          # data.* includes _rescued_data already; do not reselect it to avoid duplicate column error
          .selectExpr(&#8221;data.*&#8221;, &#8220;topic&#8221;, &#8220;partition&#8221;, &#8220;offset&#8221;, &#8220;timestamp AS kafka_timestamp&#8221;)
          .withColumn(&#8221;ingestion_ts&#8221;, current_timestamp())
    )

    return parsed</code></pre></div><p>Below is the current state of our target table. This matches the state of the diagram above. Users 1 through 4 have been ingested into the target Delta table.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yqXK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yqXK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 424w, https://substackcdn.com/image/fetch/$s_!yqXK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 848w, https://substackcdn.com/image/fetch/$s_!yqXK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 1272w, https://substackcdn.com/image/fetch/$s_!yqXK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yqXK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png" width="853" height="200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:200,&quot;width&quot;:853,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41922,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779260b0-f779-4e54-92d9-9cfc02013122_1171x200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!yqXK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 424w, https://substackcdn.com/image/fetch/$s_!yqXK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 848w, https://substackcdn.com/image/fetch/$s_!yqXK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 1272w, https://substackcdn.com/image/fetch/$s_!yqXK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626e4ec6-b311-40e7-99cc-2fde4bdec665_853x200.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Now let&#8217;s imagine the nature of our topic changes and the device and event fields need to become flexible, allowing for new nested fields to be added anytime and reflected in our target. In the current implementation, these new fields would not appear automatically in our Struct columns.</p><p>One way to allow for flexibility of these nested columns is to update the Struct columns to Variant type. As mentioned above, however, Alter Table is unavailable on a streaming table to update column types. Here&#8217;s how to accomplish a streaming table update while ensuring there is no data-loss or duplication.</p><h4>Step 1: Pause your pipeline</h4><p>Whether your pipeline is continuous or scheduled to run periodically, you don&#8217;t want to be ingesting data while performing these actions. <br><br>Under the scheduled Job:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eG7D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eG7D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 424w, https://substackcdn.com/image/fetch/$s_!eG7D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 848w, https://substackcdn.com/image/fetch/$s_!eG7D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 1272w, https://substackcdn.com/image/fetch/$s_!eG7D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eG7D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png" width="393" height="125" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:125,&quot;width&quot;:393,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12269,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/189148173?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eG7D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 424w, https://substackcdn.com/image/fetch/$s_!eG7D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 848w, https://substackcdn.com/image/fetch/$s_!eG7D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 1272w, https://substackcdn.com/image/fetch/$s_!eG7D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F236b7239-8e51-48b6-b76f-ba2e657b488c_393x125.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4>Step 2: Backup Bronze Table</h4><p>This retains all data we&#8217;ve already ingested (users 1-4), including data that no longer exists in our Kafka topic (users 1-3). We&#8217;ve now ensured we won&#8217;t lose records that have aged out of the source.</p><pre><code><code>CREATE TABLE neil_test_catalog.streaming.json_bronze_backup
SELECT * FROM neil_test_catalog.streaming.json_bronze;</code></code></pre><h4>Step 3: Determine Max Offset per Partition</h4><p>At this point, Kafka has continued to receive new records behind the scenes (user-5 and user-6). We want to ensure that when we Full Refresh our pipeline, we read only these new messages without reprocessing data (user-4). This is achieved by making use of Spark&#8217;s readStream <code>startingOffsets </code>parameter. </p><p>This parameter allows you to specify which offsets Spark should <strong>begin</strong> reading data from, <em>the first time a pipeline runs</em>. Keep in mind that in Kafka, offsets are integers that uniquely identify messages <strong>per partition</strong>, so you&#8217;ll have to specify a starting offset for each partition in your topic. SDP uses these offsets to ensure that upon initial startup, the pipeline begins exactly where you intend. From that point forward, Spark continuously records these offsets in its internal checkpoints to track progress over time and guarantee exactly-once processing. It&#8217;s also a good idea to store this Kafka metadata in the Delta table itself.</p><p>Here&#8217;s the same snapshot of our source topic and target Delta table, with offset information included:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GuUb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GuUb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 424w, https://substackcdn.com/image/fetch/$s_!GuUb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 848w, https://substackcdn.com/image/fetch/$s_!GuUb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 1272w, https://substackcdn.com/image/fetch/$s_!GuUb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GuUb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png" width="862" height="599" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:599,&quot;width&quot;:862,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46089,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GuUb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 424w, https://substackcdn.com/image/fetch/$s_!GuUb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 848w, https://substackcdn.com/image/fetch/$s_!GuUb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 1272w, https://substackcdn.com/image/fetch/$s_!GuUb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe5c843-bf29-4786-a1cd-87bd9405bc8d_862x599.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Notice that to begin processing at user-5 we will need to specify our pipeline start at offset 4. The startingOffsets parameter expects this information in the following format:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;059cdf57-c09a-47f0-b447-6a1e51336b9e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">.option(&#8221;startingOffsets&#8221;, &#8216;{&#8221;neil_struct_topic&#8221;:{&#8221;0&#8221;:4}}&#8217;)</code></pre></div><p>If our topic contained multiple partitions, starting offsets must be set for each partition and would look like this for two partitions:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b9310ebd-3020-42c3-b53b-cc58a02106cd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">.option(&#8221;startingOffsets&#8221;, &#8216;{&#8221;neil_struct_topic&#8221;:{&#8221;0&#8221;:4, &#8220;1&#8221;:6}}&#8217;)
</code></pre></div><p>But how can we find this information?</p><h4>Finding via Metadata Columns (If Defined in Pipeline and Tracked in Delta Table)</h4><p>If you&#8217;ve added Kafka metadata to your bronze table, you can retrieve your max offset per partition there. Remember that for this simple example we only have one Kafka partition. If using this method it&#8217;s important to note that these results show the most recent offset ingested, and +1 must be added to specify where Spark should <strong>start</strong> reading.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;3b7187f9-2c43-4090-9182-4205357e34df&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT partition, MAX(offset)
FROM neil_test_catalog.streaming.json_bronze
GROUP BY partition</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0pFu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0pFu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 424w, https://substackcdn.com/image/fetch/$s_!0pFu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 848w, https://substackcdn.com/image/fetch/$s_!0pFu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 1272w, https://substackcdn.com/image/fetch/$s_!0pFu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0pFu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png" width="688" height="196" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:196,&quot;width&quot;:688,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20135,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!0pFu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 424w, https://substackcdn.com/image/fetch/$s_!0pFu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 848w, https://substackcdn.com/image/fetch/$s_!0pFu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 1272w, https://substackcdn.com/image/fetch/$s_!0pFu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b343062-1b38-49b2-b258-106bc89c3a3d_688x196.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4>Finding via Spark Declarative Pipelines Checkpoints</h4><p>Another way to retrieve your offset information is to query SDP&#8217;s /checkpoints/ folder that your pipeline uses to track state and progress. For more detailed information on checkpoints and how Spark Structured Streaming achieves exactly-once processing, check out this blog: <a href="https://www.canadiandataguy.com/p/inside-delta-lakes-idempotency-magic">Inside Delta Lake&#8217;s Idempotency Magic: The Secret to Exactly-Once Spark</a>.</p><p>First, find your streaming table&#8217;s storage location via DESCRIBE DETAIL.<br><br>Note: If your table is Unity Catalog-managed, this method requires direct read access to the table&#8217;s managed storage location. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TB3u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TB3u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 424w, https://substackcdn.com/image/fetch/$s_!TB3u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 848w, https://substackcdn.com/image/fetch/$s_!TB3u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 1272w, https://substackcdn.com/image/fetch/$s_!TB3u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TB3u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png" width="1396" height="444" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:444,&quot;width&quot;:1396,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105584,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!TB3u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 424w, https://substackcdn.com/image/fetch/$s_!TB3u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 848w, https://substackcdn.com/image/fetch/$s_!TB3u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 1272w, https://substackcdn.com/image/fetch/$s_!TB3u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef457b7-710f-4ea9-9742-bb201bd52e8d_1396x444.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Using the location, you can append &#8220;/_dlt_metadata/checkpoints/<strong>your_table_name</strong>/&#8221; to find the most recent streaming query context (the greatest number). For this streaming table the max result is 8 as shown below.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;c72351ae-4363-40a7-bb0b-0e4a9ad1c060&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">path = &#8220;s3://....&#8221;
metadata_path = path + &#8220;/_dlt_metadata/checkpoints/neil_test_catalog.streaming.json_bronze/&#8221;
display(dbutils.fs.ls(metadata_path))</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qpG9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qpG9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 424w, https://substackcdn.com/image/fetch/$s_!qpG9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 848w, https://substackcdn.com/image/fetch/$s_!qpG9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 1272w, https://substackcdn.com/image/fetch/$s_!qpG9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qpG9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png" width="1456" height="694" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:694,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:212970,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd25f7e4f-dfb4-45b5-853b-5ba022965d0c_2028x888.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!qpG9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 424w, https://substackcdn.com/image/fetch/$s_!qpG9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 848w, https://substackcdn.com/image/fetch/$s_!qpG9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 1272w, https://substackcdn.com/image/fetch/$s_!qpG9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f30f6ae-941e-45fc-bfc9-6908bd30b52f_1810x863.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Within the numbered subfolder under your table name, you will see /offsets/ and /commits/ folders, each also containing numbered folders 0/, 1/, 2/ and so on. These folders represent streaming batches in SDP.</p><ul><li><p><code>offsets/N</code> is a <strong>write&#8209;ahead log entry</strong> written before processing batch N. It stores the <strong>end offsets (high&#8209;water mark)</strong> for that batch &#8212; i.e., &#8220;read up to here&#8221; for each topic/partition.</p></li><li><p><code>commits/N</code> is only written after batch N has finished successfully.</p></li></ul><p>Because an offsets/N entry can exist even if its corresponding commits/N is missing (a batch started but never committed), you should follow these steps to determine where to retrieve your startingOffsets.</p><p>List the batch IDs in both folders:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">offsets_path = path + "/_dlt_metadata/checkpoints/your_table_name/8/offsets/"
commits_path = path + "/_dlt_metadata/checkpoints/your_table_name/8/commits/"
display(dbutils.fs.ls(offsets_path))
display(dbutils.fs.ls(commits_path))</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UwIC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UwIC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 424w, https://substackcdn.com/image/fetch/$s_!UwIC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 848w, https://substackcdn.com/image/fetch/$s_!UwIC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 1272w, https://substackcdn.com/image/fetch/$s_!UwIC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UwIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png" width="1171" height="579" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:579,&quot;width&quot;:1171,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:149194,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/189148173?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F434128e2-d030-41e8-823a-07ccf1b63a36_1171x579.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!UwIC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 424w, https://substackcdn.com/image/fetch/$s_!UwIC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 848w, https://substackcdn.com/image/fetch/$s_!UwIC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 1272w, https://substackcdn.com/image/fetch/$s_!UwIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F484104d7-6833-422a-81b1-a33bc63d21e7_1171x579.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let:</p><ul><li><p><code>commit_batches</code> = all numeric batch IDs under <code>commits/</code></p></li><li><p><code>max_commit_batch</code> = largest value in <code>commit_batches</code></p></li></ul><p>Use <code>max_commit_batch</code> as the last fully committed batch, and read the matching offsets file:</p><p><strong>Always</strong> read <code>offsets/max_commit_batch</code> to get the correct <code>startingOffsets</code> JSON.</p><p>Ignore any higher batch ID that appears only under <code>offsets/</code> but not <code>commits/</code>. That batch started but never finished, so if you treat its offsets as your starting position, Spark will behave as if that data has already been read and will <strong>skip</strong> it rather than processing it.</p><p>In my example, both the /offsets/ and /commits/ folders contain only batches 0 and 1, so using <code>max_commit_batch,</code> we read startingOffsets from /offsets/1.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;5e16f2a8-9c27-4b1a-b3ae-e66ef265589f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">metadata_path = path + &#8220;/_dlt_metadata/checkpoints/neil_test_catalog.streaming.json_bronze/8/offsets/1/&#8221;
display(dbutils.fs.head(metadata_path))</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;45573231-4ab2-4ab6-b675-b5b5cdd1ed08&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{&#8221;batchWatermarkMs&#8221;:0,&#8221;batchTimestampMs&#8221;:1771439553201,&#8221;conf&#8221;:{...}}
{&#8221;neil_struct_topic&#8221;:{&#8221;0&#8221;:4}}</code></pre></div><p>Notice the final line is our starting partition:offset information in the exact format we created manually above. Spark tracks the next offset to read, so you do not need to increment +1 via this method. Again, if our topic had multiple partitions it might look like:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;d18d4bc3-3a9f-4d23-afb3-bbc85e9cf333&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{&#8221;neil_struct_topic&#8221;:{&#8221;0&#8221;:4, &#8220;1&#8221;:6}}</code></pre></div><h4>Step 4: Update pipeline definition</h4><p>Now it&#8217;s time to apply the actual logic changes that prompted this process. For my example, I add variant support to table properties:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">table_properties={&#8221;quality&#8221;: &#8220;bronze&#8221;, &#8220;delta.feature.variantType-preview&#8221;: &#8220;supported&#8221;}</code></pre></div><p>And cast the Struct columns to Variant:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">return (
        parsed
            .withColumn(&#8221;event&#8221;, expr(&#8221;parse_json(to_json(event))&#8221;))
            .withColumn(&#8221;device&#8221;, expr(&#8221;parse_json(to_json(device))&#8221;))
        )</code></pre></div><h4>Step 5: Full Refresh Table</h4><p>With pipeline logic updated, it&#8217;s time to run with Full refresh to wipe the target table and ingest the new Kafka records starting at our specified offsets. The resulting table will contain only the records we did not back up via Step 2.</p><p>To do this, we run our pipeline with full refresh after adding our startingOffsets (line 28). Here is the final pipeline definition.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;6c55857d-d0f1-47b3-a53d-d6592eae648e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">.option(&#8221;startingOffsets&#8221;, &#8216;{&#8221;neil_struct_topic&#8221;:{&#8221;0&#8221;:4}}&#8217;)</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;152abf3a-b98c-4704-9e92-8f5622e075cb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import dlt
from pyspark.sql.functions import current_timestamp, col, from_json, expr

SERVERS = &#8220;REDACTED&#8221;
TOPIC = &#8220;neil_struct_topic&#8221;

# Explicit JSON schema for predictable demos (no schemaLocationKey)
SCHEMA_DDL = &#8220;&#8221;&#8220;
  user STRING,
  email STRING,
  country STRING,
  device STRUCT&lt;os: STRING, model: STRING, geo: STRUCT&lt;lat: DOUBLE, lon: DOUBLE&gt;&gt;,
  event STRUCT&lt;name: STRING, seq: BIGINT, ts: STRING&gt;
&#8220;&#8221;&#8220;

@dlt.table(
    name=&#8221;json_bronze&#8221;,
    comment=&#8221;Raw Kafka payload with explicit JSON schema and rescued data&#8221;,
    table_properties={&#8221;quality&#8221;: &#8220;bronze&#8221;, &#8220;delta.feature.variantType-preview&#8221;: &#8220;supported&#8221;}
)
def json_bronze():
    df = (
        spark.readStream
            .format(&#8221;kafka&#8221;)
            .option(&#8221;kafka.bootstrap.servers&#8221;, SERVERS)
            .option(&#8221;kafka.security.protocol&#8221;, &#8220;SSL&#8221;)
            .option(&#8221;subscribe&#8221;, TOPIC)
            .option(&#8221;startingOffsets&#8221;, &#8216;{&#8221;neil_struct_topic&#8221;:{&#8221;0&#8221;:4}}&#8217;)
            .load()
    )

    parsed = (
        df.selectExpr(&#8221;CAST(value AS STRING) AS json_str&#8221;, &#8220;topic&#8221;, &#8220;partition&#8221;, &#8220;offset&#8221;, &#8220;timestamp&#8221;)
          .select(
              from_json(
                  col(&#8221;json_str&#8221;),
                  SCHEMA_DDL,
                  options={&#8221;rescuedDataColumn&#8221;: &#8220;_rescued_data&#8221;}  # capture type mismatches/new fields
              ).alias(&#8221;data&#8221;),
              &#8220;topic&#8221;, &#8220;partition&#8221;, &#8220;offset&#8221;, &#8220;timestamp&#8221;
          )
          # data.* includes _rescued_data already; do not reselect it to avoid duplicate column error
          .selectExpr(&#8221;data.*&#8221;, &#8220;topic&#8221;, &#8220;partition&#8221;, &#8220;offset&#8221;, &#8220;timestamp AS kafka_timestamp&#8221;)
          .withColumn(&#8221;ingestion_ts&#8221;, current_timestamp())
    )

    return (
        parsed
            .withColumn(&#8221;event&#8221;, expr(&#8221;parse_json(to_json(event))&#8221;))
            .withColumn(&#8221;device&#8221;, expr(&#8221;parse_json(to_json(device))&#8221;))
        )</code></pre></div><p>On the pipeline page:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!A22d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A22d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 424w, https://substackcdn.com/image/fetch/$s_!A22d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 848w, https://substackcdn.com/image/fetch/$s_!A22d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 1272w, https://substackcdn.com/image/fetch/$s_!A22d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!A22d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png" width="396" height="219" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:219,&quot;width&quot;:396,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33568,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/189148173?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!A22d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 424w, https://substackcdn.com/image/fetch/$s_!A22d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 848w, https://substackcdn.com/image/fetch/$s_!A22d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 1272w, https://substackcdn.com/image/fetch/$s_!A22d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F287e92dd-2a47-4cc9-b03f-612e38730a73_396x219.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Here&#8217;s the result: user-5 and user-6 as expected:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2vh_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2vh_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 424w, https://substackcdn.com/image/fetch/$s_!2vh_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 848w, https://substackcdn.com/image/fetch/$s_!2vh_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 1272w, https://substackcdn.com/image/fetch/$s_!2vh_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2vh_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png" width="909" height="127" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:127,&quot;width&quot;:909,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24239,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2543971d-6d72-4e6c-b276-43fc19af5dff_1176x127.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!2vh_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 424w, https://substackcdn.com/image/fetch/$s_!2vh_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 848w, https://substackcdn.com/image/fetch/$s_!2vh_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 1272w, https://substackcdn.com/image/fetch/$s_!2vh_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5471779-6f11-42d8-8d0b-1b4d8c58c3f6_909x127.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4>Step 6: Insert Historical Records</h4><p>To complete our intended result table, insert historical data from the backup table, ensuring the data matches the new table format (cast columns, etc.):</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;591001a1-3fec-4a05-9d0b-6af6a7eb9d12&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">INSERT INTO neil_test_catalog.streaming.json_bronze
SELECT user, email, country, to_variant_object(device), to_variant_object(event), _rescued_data, topic, partition, offset, kafka_timestamp, ingestion_ts
FROM neil_test_catalog.streaming.json_bronze_backup</code></pre></div><p>Our final result. Your pipeline may be resumed.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;95091137-7fc2-4fac-b81c-bc3bd107ffb9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">user:string
email:string
country:string
device:variant
event:variant
_rescued_data:string
topic:string
partition:integer
offset:long
kafka_timestamp:timestamp
ingestion_ts:timestamp</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x3gO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x3gO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 424w, https://substackcdn.com/image/fetch/$s_!x3gO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 848w, https://substackcdn.com/image/fetch/$s_!x3gO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 1272w, https://substackcdn.com/image/fetch/$s_!x3gO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!x3gO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png" width="948" height="224" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:224,&quot;width&quot;:948,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:61678,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://neilwilsondata.substack.com/i/188168915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8eb96d1-3eb6-40c1-8a4a-9a7c91c5513f_1303x224.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!x3gO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 424w, https://substackcdn.com/image/fetch/$s_!x3gO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 848w, https://substackcdn.com/image/fetch/$s_!x3gO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 1272w, https://substackcdn.com/image/fetch/$s_!x3gO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d3d7f02-263e-49ae-a838-8cc8826ac96c_948x224.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4>Step 7: Revert startingOffsets</h4><p>To avoid pipeline failures in the case of a future Full Refresh, revert startingOffsets to its prior value.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;693848c8-f57d-4c01-b5cf-9214847e70ea&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">.option(&#8221;startingOffsets&#8221;, &#8220;earliest&#8221;)</code></pre></div><h3>Frequently Asked Questions</h3><p><strong>Q: Why can&#8217;t I just run a standard Full Refresh on the pipeline?</strong> </p><p><strong>A:</strong> A Full Refresh clears the target table and re-reads the source from the beginning. If your Kafka topic has a retention policy (TTL), data that has "aged out" of the topic will be permanently lost because it no longer exists in the source to be re-read.</p><p><strong>Q: Why is </strong><code>startingOffsets</code><strong> necessary if I have a backup?</strong> </p><p><strong>A:</strong> While the backup saves your history, <code>startingOffsets</code> ensures your pipeline resumes reading <em>exactly</em> where the backup stopped. Without this explicit instruction, the pipeline might default to "earliest" (reading only what remains in Kafka, creating a gap) or "latest" (skipping data that arrived during the maintenance window).</p><p><strong>Q: Is this process required for "Append-Only" tables?</strong> </p><p><strong>A:</strong> Generally, yes, if you need to restructure the existing table. If you are only adding new columns that are nullable, you might rely on schema evolution, but fundamental type changes usually require the table to be rewritten.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Cutting Token Costs Reaches the Renaissance]]></title><description><![CDATA[A Lakebase Powered Solution for Enforcing Token Budgets, Now with Fewer Sharp Edges]]></description><link>https://www.databricksters.com/p/cutting-token-costs-reaches-the-renaissance</link><guid isPermaLink="false">https://www.databricksters.com/p/cutting-token-costs-reaches-the-renaissance</guid><dc:creator><![CDATA[Austin]]></dc:creator><pubDate>Tue, 17 Mar 2026 14:02:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7sNy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><span>UPDATE:</span></strong><span> This is now obsolete with the new </span><a href="https://docs.databricks.com/aws/en/ai-gateway/budgets-beta"><span>Unity AI Gateway budgeting functionality</span></a><span>. This was a fun passion project, and I appreciate your interest in my work, but now that this has been formally baked into the product, I would advise you stop reading here. The below is left as-is for reference. </span></p><div><hr></div><p>Back in October I published a blog called <a href="https://www.databricksters.com/p/getting-medieval-on-token-costs">Getting Medieval on Token Costs</a>. The code and strategy I provided worked, but as the title implied it was rough around the edges. How rough? Well&#8230;</p><ul><li><p>The API calls to the FMs were synchronous, so QPS would have been Medieval indeed</p></li><li><p>The Lakebase instance was provisioned, so it would always be accruing costs even without usage</p></li><li><p>There was no UI, so you or your admin would be spending hours fiddling with thousand line SQL queries for enterprise use cases</p></li></ul><p>But no matter, we&#8217;ve had a Renaissance!</p><p><a href="https://github.com/azaccor/token-rate-limiter">The repo</a> got three meaningful updates and a handful of smaller ones that collectively move this from being technically functional to something a medium enterprise team might actually want to use on Databricks.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7sNy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7sNy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7sNy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7sNy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7sNy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7sNy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg" width="1456" height="1532" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1532,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11842916,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/190565336?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7sNy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7sNy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7sNy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7sNy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13774bb4-e2c9-4cf2-a151-17fe03cd9b73_5786x6090.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The Money Changer and His Wife - Quentin Matsys, 1514 oil-on-panel</figcaption></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.databricksters.com/subscribe?"><span>Subscribe now</span></a></p><h3><strong>Quick</strong> <strong>Refresher</strong></h3><p>The original solution uses a custom MLflow model serving endpoint as a proxy between your users and whatever foundation model they&#8217;re calling. Before the request hits the FM, the endpoint checks two Lakebase tables: one for the user&#8217;s token limit and another for how many tokens they&#8217;ve already burned through. If they&#8217;re over budget, the request ends up like John the Baptist in the cover image. If not, it goes through to the FM and the usage is written back to Lakebase along with the response and remaining balance.</p><h3><strong>Change</strong> <strong>1:</strong> <strong>Autoscaling</strong> <strong>Lakebase</strong></h3><p>The original code used a Provisioned Lakebase instance because that was the only option at the time, but it&#8217;s going away and we have something better. Autoscaling Lakebase. </p><p>Swapping to Autoscaling Lakebase means you can set minimum and maximum scaling bands, and if you don&#8217;t need a high availability instance, it will also allow you to scale to zero during times of no use. </p><p>This is the smallest change architecturally, but it&#8217;s nice not to pay for compute we don&#8217;t need.</p><h3><strong>Change</strong> <strong>2:</strong> <strong>ResponsesAgent</strong> <strong>+</strong> <strong>Async</strong> <strong>FM</strong> <strong>Calls</strong></h3><p>The original code used <code>mlflow.pyfunc.PythonModel</code> and called the FM endpoint via <code>requests.post()</code>, which is synchronous and blocking. Only one request can be handled at a time per unit of concurrency. Which meant the endpoint that was supposed to help you manage costs via budgeting is throttling your throughput instead. While I suppose that is one way to reduce token costs, it&#8217;s not very useful.</p><p>The new version replaces the PythonModel with a ResponsesAgent and swaps <code>requests</code> for <code>httpx.AsyncClient</code> inside an <code>async def predict_stream()</code>. Now multiple FM calls can be in flight simultaneously and the serving endpoint isn&#8217;t waiting on one user&#8217;s 20-second Claude response before it can look at the next request in the queue.</p><p>The core logic now lives in a standalone <code>rate_limiter_agent.py</code> decoupled from the notebook. The public API is much cleaner:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;2ec01639-67a8-440b-bc77-590a75edefd6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">  agent = TokenRateLimiterAgent(
      db_config={...},
      workspace_client=WorkspaceClient(),
      endpoint_name="ep-your-endpoint",
      group_members={
          "data-science-team": ["andrea@company.com", "john@company.com"],
      },
  )

  # Before calling the FM:
  quota = agent.check_quota("andrea@company.com", "databricks-claude-sonnet-4-5")
  if not quota["allowed"]:
      # Return 429 or block the request
      ...

  # After the FM call completes:
  agent.log_usage(
      user_name="andrea@company.com",
      model_name="databricks-claude-sonnet-4-5",
      prompt_tokens=1200,
      completion_tokens=350,
      request_id="req-abc123",
  )
</code></pre></div><p>You can drop this into any existing pipeline without touching the notebook, which certainly helps if you&#8217;re integrating this into something that already has its own serving infrastructure.</p><h3><strong>Change</strong> <strong>3:</strong> <strong>An</strong> <strong>Actual</strong> <strong>Frontend</strong></h3><p>The original had no management UI, which meant you had to set limits by writing manual SQL queries for any new change to your budgeting policy. Not very convenient. </p><p>The new repo ships a full Databricks App: a React + FastAPI application that deploys alongside your serving endpoint and gives administrators a no-code interface for setting granular budgets. How granular you ask? Any combination of:</p><ul><li><p>A user, service principal, or group</p></li><li><p>Calling any FM, list of FMs, or across all FMs in the workspace</p></li><li><p>That resets every X hours, days, weeks, months, or never</p></li><li><p>Limited to a specified count of tokens or dollars</p><ul><li><p>Pre-populates token costs from Databricks documentation, but manually editable in case this changes or you have some kind of secret discount I don&#8217;t know about</p></li><li><p>This is another nice quality of life feature since tokens are not all created equally; GPT OSS 20B tokens cost about 100x less than GPT 5.4 tokens</p></li></ul></li></ul><p>The drop-downs auto-populate users, SPs, and groups as well as the Databricks Foundation Models.</p><p>It also comes with a handy monitoring dashboard so you can see usage over time, your top consumers, and the most popular models.</p><p>The App authenticates to Lakebase via a native Postgres role with a static password stored in Databricks Secrets, so there&#8217;s no OAuth token refresh to manage.</p><h3><strong>An</strong> <strong>Honest</strong> <strong>Conclusion</strong></h3><p>Is this production-grade for an org running thousands of concurrent end users? Maybe not. You might consider mini-batching requests at scale, but there will still be some amount of cost tracking overhead, and this gets more difficult at scale.</p><p>Is this production-grade for most actual enterprise teams who want to stop their power users from accidentally burning through their monthly token budget in a week? Yes. I think this solution really shines when you have dozens to hundreds of daily active users who might get greedy on Opus requests without some budget enforcement. </p><p>But don&#8217;t take my word for it; check it out for yourself. The code <a href="https://github.com/azaccor/token-rate-limiter">lives here</a> and setup instructions are in the README.</p><p>Cheers and happy coding.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Migrating Existing Dashboards to Databricks AI/BI, Part 2: Filter Actions, Cross-Filtering, and Drill-Through]]></title><description><![CDATA[How to connect visuals, enable cross-filtering, and drill into details in Databricks AI/BI Dashboards]]></description><link>https://www.databricksters.com/p/migrating-existing-dashboards-to-482</link><guid isPermaLink="false">https://www.databricksters.com/p/migrating-existing-dashboards-to-482</guid><dc:creator><![CDATA[Artem Chebotko]]></dc:creator><pubDate>Tue, 10 Mar 2026 15:02:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gLl3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gLl3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gLl3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!gLl3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!gLl3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!gLl3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gLl3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gLl3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!gLl3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!gLl3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!gLl3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4b6aea-ff9b-4eb0-a60e-9e2338aefa0e_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As a Specialist Solutions Architect at Databricks, I often hear the same questions from customers who are migrating dashboards from legacy BI tools to Databricks AI/BI Dashboards:</p><ul><li><p><em>&#8220;What&#8217;s the Databricks equivalent of the context filters we use today?&#8221;</em></p></li><li><p><em>&#8220;Can we still do cascading filters where each dropdown only shows relevant values?&#8221;</em></p></li><li><p><em>&#8220;Do you support filter actions when I click on a bar or a point?&#8221;</em></p></li><li><p><em>&#8220;How do we do user-based filtering in AI/BI Dashboards?&#8221;</em></p></li></ul><p>In the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">first blog post in this series</a>, I focused on the first two questions and showed how to recreate:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>context filters using parameters in dataset SQL, and</p></li><li><p>&#8220;<em>Only Relevant Values</em>&#8221; filters using field filters and query-based parameters.</p></li></ul><p>This blog tackles the third question: &#8220;<em>How do we replace filter actions from existing dashboards when we click on a bar/segment/point?</em>&#8221;</p><p>In many BI tools, this behavior is configured as a <strong>filter action</strong> (a click on a mark filters other views). In Databricks AI/BI Dashboards, the equivalent interactivity is split into two built-in features:</p><ul><li><p><strong>Cross-filtering</strong>, where clicking a mark in one chart filters other charts on the same page that use the same dataset.</p></li><li><p><strong>Drill-through</strong>, where right-clicking a mark opens a target page filtered to that selection.</p></li></ul><p>Once you combine cross-filtering and drill-through with the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">context and cascading patterns</a>, you can reproduce most real-world filter-action workflows.</p><p>As before, I&#8217;ll use the built-in <code>samples.tpch</code> dataset. I&#8217;ve also published the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, so you can follow along and inspect the configurations yourself.</p><h3><strong>Recap: TPCH Sales dataset</strong></h3><p>To keep examples concrete, we&#8217;ll use the TPCH sample data that ships with Databricks in the <code>samples.tpch</code> schema. I&#8217;ll reuse the same base dataset, <em>TPCH Sales</em>, from the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">first blog post</a>, which joins tables <code>region</code>, <code>nation</code>, <code>customer</code>, <code>orders</code>, and <code>lineitem</code>, and computes revenue:</p><pre><code>SELECT
  r.r_name              AS region,
  n.n_name              AS nation,
  c.c_custkey           AS customer_id,
  c.c_name              AS customer_name,
  o.o_orderkey          AS order_id,
  o.o_orderdate         AS order_date,
  l.l_extendedprice * (1 - l.l_discount) AS revenue
FROM samples.tpch.region   AS r
JOIN samples.tpch.nation   AS n ON n.n_regionkey = r.r_regionkey
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
JOIN samples.tpch.orders   AS o ON o.o_custkey   = c.c_custkey
JOIN samples.tpch.lineitem AS l ON l.l_orderkey  = o.o_orderkey;</code></pre><p>In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, this is the <em>TPCH Sales</em> dataset. It and its derivatives are used by multiple pages:</p><ul><li><p><em>Context filter</em></p></li><li><p><em>Cascading filters with field filters</em></p></li><li><p><em>Cascading filters with query-based parameters</em></p></li><li><p><em>Cross-filtering </em>(new in this post)</p></li><li><p><em>Drill-through details </em>(new in this post)</p></li><li><p>and others</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZQnz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZQnz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 424w, https://substackcdn.com/image/fetch/$s_!ZQnz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 848w, https://substackcdn.com/image/fetch/$s_!ZQnz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 1272w, https://substackcdn.com/image/fetch/$s_!ZQnz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZQnz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png" width="1456" height="673" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dff3310d-8eba-4213-b83e-e5244c972269_1600x740.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:673,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZQnz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 424w, https://substackcdn.com/image/fetch/$s_!ZQnz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 848w, https://substackcdn.com/image/fetch/$s_!ZQnz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 1272w, https://substackcdn.com/image/fetch/$s_!ZQnz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdff3310d-8eba-4213-b83e-e5244c972269_1600x740.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Filter actions vs. cross-filtering and drill-through</strong></h3><p>Before we build anything, it helps to align vocabulary.</p><p>In many traditional BI tools, filter actions are configured explicitly:</p><ul><li><p>You specify one or more source sheets.</p></li><li><p>You specify one or more target sheets.</p></li><li><p>You choose which fields are passed as filters.</p></li><li><p>You choose what happens when the selection is cleared.</p></li></ul><p>In Databricks AI/BI Dashboards, cross-filtering is implicit. You don&#8217;t turn it on in the visualization panel.</p><ul><li><p>It is automatically applied to supported visualization types that use the same dataset.</p></li><li><p>When you click a bar, slice, or point, AI/BI adds a filter based on that value and re-runs all other visualizations on the page that share the dataset.</p></li></ul><p>In Databricks AI/BI Dashboards, drill-through is also implicit, but slightly more structured:</p><ul><li><p>When you right-click a supported chart type, AI/BI shows <em>Drill to &#8594; &lt;target page&gt;</em> if there is another page in the dashboard that uses the same dataset.</p></li><li><p>The target page opens with all visuals based on that dataset filtered to the selected segment, and any compatible filters on that dataset are auto-populated.</p></li></ul><p>Conceptually:</p><ul><li><p>Cross-filtering &#8776; a within-page filter action.</p></li><li><p>Drill-through &#8776; a navigation filter action (summary &#8594; details).</p></li></ul><p>The rest of the post walks through how to configure your pages so these implicit behaviors &#8220;just work&#8221;.</p><h3><strong>1. Recreating within-page filter actions with cross-filtering</strong></h3><p>A very common dashboard pattern is:</p><ul><li><p>A summary bar chart (for example, total revenue by nation).</p></li><li><p>One or more supporting charts (for example, revenue share by region, or a donut chart for mix).</p></li><li><p>A filter action so clicking a bar filters the other visuals.</p></li></ul><p>In AI/BI Dashboards, this becomes cross-filtering on top of the <em>TPCH Sales</em> dataset.</p><h4><strong>1.1. When cross-filtering is applied</strong></h4><p>Cross-filtering is applied automatically when all of the following are true:</p><ul><li><p>The visualizations are on the same page.</p></li><li><p>The visualizations use the same dataset (for example, <em>TPCH Sales</em>).</p></li><li><p>The visualization type is one of the supported chart types: <em>Bar</em>, <em>Box plot</em>, <em>Heatmap</em>, <em>Histogram</em>, <em>Pie</em>, <em>Scatter</em>, or <em>Point map</em>.</p></li></ul><p>If those conditions are met, there is nothing to enable. Clicks on supported charts become filters for any other visualizations on the page that use that dataset.</p><h4><strong>1.2. Build the </strong><em><strong>Cross-filtering</strong></em><strong> page</strong></h4><p>In your AI/BI dashboard, add a page named <em>Cross-filtering</em>.</p><p>On this page:</p><ol><li><p>Add a bar chart: <em>Revenue by nation</em></p><ul><li><p>Visualization: <em>Bar</em></p></li><li><p>Dataset = <em>TPCH Sales</em></p></li><li><p>X axis: <code>nation</code></p></li><li><p>Y axis: <code>SUM(revenue)</code></p></li></ul></li><li><p>Add a pie: <em>Revenue by region</em></p><ul><li><p>Visualization: <em>Pie</em></p></li><li><p>Dataset = <em>TPCH Sales</em></p></li><li><p>Slice by (Color): <code>region</code></p></li><li><p>Value (Angle): <code>SUM(revenue)</code></p></li></ul></li><li><p>Add filters (optional)</p><ul><li><p>Add a <em>Region</em> filter on <code>TPCH Sales.region</code></p></li><li><p>Add a <em>Nation</em> filter on <code>TPCH Sales.nation</code></p></li></ul></li></ol><p>These are just standard filter widgets. There is no &#8220;cross-filtering&#8221; toggle anywhere in the configuration.</p><p>In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, this page is already built for you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aM9L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aM9L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 424w, https://substackcdn.com/image/fetch/$s_!aM9L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 848w, https://substackcdn.com/image/fetch/$s_!aM9L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 1272w, https://substackcdn.com/image/fetch/$s_!aM9L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aM9L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png" width="1456" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aM9L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 424w, https://substackcdn.com/image/fetch/$s_!aM9L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 848w, https://substackcdn.com/image/fetch/$s_!aM9L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 1272w, https://substackcdn.com/image/fetch/$s_!aM9L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7030f297-722f-4ebe-b522-b20ff0bc75b3_1600x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4><strong>1.3. Use cross-filtering on the charts</strong></h4><p>Try the following workflow:</p><ol><li><p>Select two regions, <em>AFRICA</em> and <em>ASIA</em>, on the <em>Revenue by region</em> chart.</p></li></ol><blockquote></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xwfZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xwfZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 424w, https://substackcdn.com/image/fetch/$s_!xwfZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 848w, https://substackcdn.com/image/fetch/$s_!xwfZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 1272w, https://substackcdn.com/image/fetch/$s_!xwfZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xwfZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png" width="1456" height="586" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:586,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xwfZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 424w, https://substackcdn.com/image/fetch/$s_!xwfZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 848w, https://substackcdn.com/image/fetch/$s_!xwfZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 1272w, https://substackcdn.com/image/fetch/$s_!xwfZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc420da58-ce61-418f-8cbb-35d3494b7e3e_1600x644.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ol start="2"><li><p>Click the bar for <em>JAPAN</em> in the <em>Revenue by nation</em> chart.</p></li></ol><blockquote></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!N1P1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N1P1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 424w, https://substackcdn.com/image/fetch/$s_!N1P1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 848w, https://substackcdn.com/image/fetch/$s_!N1P1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 1272w, https://substackcdn.com/image/fetch/$s_!N1P1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!N1P1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png" width="1456" height="586" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:586,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!N1P1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 424w, https://substackcdn.com/image/fetch/$s_!N1P1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 848w, https://substackcdn.com/image/fetch/$s_!N1P1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 1272w, https://substackcdn.com/image/fetch/$s_!N1P1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f12352e-c307-47a3-8cd6-e76126084dd5_1600x644.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Because cross-filtering is automatic for supported charts that share a dataset:</p><ul><li><p>AI/BI adds filters <em>Nation: JAPAN</em> and <em>Region: AFRICA, ASIA</em> to the <em>TPCH Sales</em> dataset for this page.</p></li><li><p>Both charts get updated accordingly.</p></li></ul><p>You can <em>Reset all to default</em> and try a different filter combination.</p><p>From a migration perspective, this answers a common question: &#8220;<em>Can clicking a bar automatically update the rest of the dashboard in AI/BI?</em>&#8221; Yes &#8211; when charts share a dataset and use supported visualization types, cross-filtering works implicitly with no additional configuration.</p><h3><strong>2. Recreating across-page filter actions with drill-through</strong></h3><p>Another classic <em>summary &#8594; detail</em> filter-action pattern is:</p><ul><li><p>A summary view (for example, revenue by nation).</p></li><li><p>A detail view (for example, individual orders).</p></li><li><p>A filter action that passes the selected value into the detail sheet as a filter.</p></li></ul><p>In AI/BI Dashboards, this is implemented as drill-through.</p><h4><strong>2.1. How drill-through is applied</strong></h4><p>Drill-through shows up as a right-click option when several conditions are satisfied.</p><ul><li><p>The source chart is a supported type: <em>Bar</em>, <em>Box plot</em>, <em>Heatmap</em>, <em>Histogram</em>, <em>Pie</em>, <em>Scatter</em>, or <em>Point map</em>.</p></li><li><p>There is at least one target page in the same dashboard where:</p><ul><li><p>At least one visualization uses the same dataset as the source chart.</p></li><li><p>The field you click on has a compatible filter or column on the target page.</p></li></ul></li></ul><p>In recent AI/BI releases, drill-through no longer requires an explicit target filter; any visualization based on the same dataset as the source selection is filtered automatically, and filters (if they exist) are populated with the drilled values.</p><p>There is no drill-through toggle in the widget side panel. Once the above conditions are true, AI/BI surfaces <em>Drill to &#8594; &lt;page name&gt;</em> in the context menu automatically.</p><h4><strong>2.2. Build the </strong><em><strong>Drill-through details</strong></em><strong> page</strong></h4><p>In the same dashboard, add another page named <em>Drill-through details</em>.</p><p>On this page:</p><ol><li><p>Add a detail table visualization</p><ul><li><p>Visualization: <em>Table</em></p></li><li><p>Dataset = <em>TPCH Sales</em></p></li><li><p>Columns: <em>region</em>, <em>nation</em>, <em>customer_id</em>, <em>customer_name</em>, <em>order_id</em>, <em>order_date</em>, <em>revenue</em></p></li></ul></li><li><p>Add filters (optional)</p><ul><li><p>Add a <em>Region</em> filter on <code>TPCH Sales.region</code></p></li><li><p>Add a <em>Nation</em> filter on <code>TPCH Sales.nation</code></p></li><li><p>Add a <em>Customer</em> filter on <code>TPCH Sales.customer_id</code></p></li></ul></li></ol><p>Again, there is no special drill-through configuration here &#8211; just a normal page that uses the same dataset.</p><p>In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, this page is already built for you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d5DG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d5DG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 424w, https://substackcdn.com/image/fetch/$s_!d5DG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 848w, https://substackcdn.com/image/fetch/$s_!d5DG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 1272w, https://substackcdn.com/image/fetch/$s_!d5DG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!d5DG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png" width="1456" height="648" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:648,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d5DG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 424w, https://substackcdn.com/image/fetch/$s_!d5DG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 848w, https://substackcdn.com/image/fetch/$s_!d5DG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 1272w, https://substackcdn.com/image/fetch/$s_!d5DG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87d8d760-8221-47f5-a7d5-b994033d2da3_1504x669.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4><strong>2.3. Drill from summary to details</strong></h4><p>Go back to the <em>Cross-filtering</em> page and right-click a bar in the <em>Revenue by nation</em> chart (for example, <em>UNITED STATES</em>).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4-aE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4-aE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 424w, https://substackcdn.com/image/fetch/$s_!4-aE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 848w, https://substackcdn.com/image/fetch/$s_!4-aE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 1272w, https://substackcdn.com/image/fetch/$s_!4-aE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4-aE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png" width="1456" height="820" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:820,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4-aE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 424w, https://substackcdn.com/image/fetch/$s_!4-aE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 848w, https://substackcdn.com/image/fetch/$s_!4-aE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 1272w, https://substackcdn.com/image/fetch/$s_!4-aE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe7d29c2-1a06-494c-822a-2152aefea12c_1502x846.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>After clicking <em>Drill to &#8594; Drill-through details</em>, AI/BI opens the <em>Drill-through details</em> page and filters all visualizations based on <em>TPCH Sales</em> to <em>Nation: UNITED STATES</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QvbJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QvbJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 424w, https://substackcdn.com/image/fetch/$s_!QvbJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 848w, https://substackcdn.com/image/fetch/$s_!QvbJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 1272w, https://substackcdn.com/image/fetch/$s_!QvbJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QvbJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png" width="1456" height="646" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:646,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QvbJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 424w, https://substackcdn.com/image/fetch/$s_!QvbJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 848w, https://substackcdn.com/image/fetch/$s_!QvbJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 1272w, https://substackcdn.com/image/fetch/$s_!QvbJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4964f70f-2bfb-4050-8762-aba6cf0081c3_1501x666.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>From the user&#8217;s perspective, this feels almost identical to a filter action in traditional dashboards that navigates from a summary sheet to a detailed sheet with the selected country carried over.</p><h3><strong>Cross-filtering vs. drill-through vs. filter widgets</strong></h3><p>Across this blog and the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">first blog</a>, you now have three main interaction tools in AI/BI Dashboards:</p><ol><li><p>Filter widgets</p><ul><li><p>Context filters, cascading filters, and query-based parameters.</p></li><li><p>Best for primary, always-visible controls like <em>Region</em>, <em>Date</em>, <em>Product</em>.</p></li></ul></li><li><p>Cross-filtering</p><ul><li><p>Click data points in a supported visualization to filter other charts on the page that use the same dataset.</p></li><li><p>Best for ad-hoc exploration, answering questions like &#8220;<em>What happens if I focus only on this region?</em>&#8221; and &#8220;<em>Which nations are driving that spike?</em>&#8221;.</p></li></ul></li><li><p>Drill-through</p><ul><li><p>Right-click a mark to open another page already filtered to that selection.</p></li><li><p>Best for guided summary-to-detail flows where you don&#8217;t want to cram everything onto one page.</p></li></ul></li></ol><p>A simple migration rule of thumb:</p><ul><li><p>Use filter widgets to rebuild the core filter panels from your existing dashboards.</p></li><li><p>Use cross-filtering to automatically filter other visualizations on the page based on chart interactions.</p></li><li><p>Use drill-through to replace &#8220;go to sheet&#8221; filter actions and connect high-level KPIs to detail pages.</p></li></ul><h3><strong>Summary and what&#8217;s next</strong></h3><p>In this second blog post in the series, we answered: &#8220;<em>Do you support filter actions when I click on a bar or a point?</em>&#8221;</p><p>The short answer is <em>yes</em>:</p><ul><li><p><strong>Cross-filtering</strong> lets viewers click on supported charts to filter all other visualizations on the same page that share a dataset &#8211; no configuration required.</p></li><li><p><strong>Drill-through</strong> lets viewers right-click a mark and open another page where visuals on the same dataset are already filtered to the selected values, and any matching filters are pre-populated.</p></li></ul><p>Combined with the patterns from the <a href="https://www.databricksters.com/p/migrating-existing-dashboards-to">first blog post</a> &#8211; context filters and cascading &#8220;<em>Only Relevant Values</em>&#8221; filters &#8211; you now have a robust toolkit for recreating the interactive filtering experience your users expect from traditional dashboards inside Databricks AI/BI Dashboards.</p><p>The <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a> now includes: a context filter page, a field-based cascading page, a query-based cascading page, a cross-filtering page, and a drill-through details page. You can import it into your workspace and adapt the patterns to your own datasets.</p><p>In the next post in this series, I&#8217;ll tackle the remaining migration question: &#8220;<em>How do we do user-based filtering and row-level security in AI/BI Dashboards?</em>&#8221;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Ingest Postgres into your LakeHouse with LakeFlow]]></title><description><![CDATA[Get the configuration json to customize your pipelines.]]></description><link>https://www.databricksters.com/p/ingest-postgres-into-your-lakehouse</link><guid isPermaLink="false">https://www.databricksters.com/p/ingest-postgres-into-your-lakehouse</guid><dc:creator><![CDATA[Nishant Deshpande]]></dc:creator><pubDate>Tue, 03 Mar 2026 06:27:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zPJJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff49ecae-7c56-403c-9389-61b28de6a50f_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Databricks Lakeflow Connect can sync multiple postgres databases to the Lakehouse. This post shows how to set up a single pipeline with multiple source databases and multiple target UC schemas, and specify compute size to minimize costs. </p><p>The first section explains how replication from Postgres works, and how to configure it. The second part show how to configure Lakeflow pipelines in your Databricks workspace to ingest from Postgres.</p><div><hr></div><h2><strong>How PostgreSQL logical replication works</strong></h2><p>PostgreSQL writes every change (insert, update, delete) to the <strong>Write-Ahead Log (WAL)</strong> before applying it. Logical replication decodes that WAL stream into a human-readable format that consumers can subscribe to.</p><p>Three objects are involved:</p><h3><strong>1. WAL level</strong></h3><p>Logical replication requires <code>wal_level = logical</code>. On AWS RDS, set <code>rds.logical_replication = 1</code> in your parameter group (requires a reboot).</p><h3><strong>2. Publication</strong></h3><p>A <strong>publication</strong> is a named filter over which tables to expose for replication. It lives inside a single database.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;3059aeb3-7c20-4360-a422-78c8d94fbc27&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Replicate specific tables
CREATE PUBLICATION mfg_paloalto_cdc_pub FOR TABLE iot1.devices, iot1.alerts;

-- Or replicate all tables in the database
CREATE PUBLICATION mfg_paloalto_cdc_pub FOR ALL TABLES;</code></pre></div><p>One publication per database is typical. If you have multiple PostgreSQL databases on the same server, each needs its own publication.</p><h3><strong>3. Replication slot</strong></h3><p>A <strong>replication slot</strong> is a cursor into the WAL. PostgreSQL retains WAL segments until the slot consumer confirms it has processed them (<code>confirmed_flush_lsn</code>). This guarantees the consumer never misses a change, even if it disconnects temporarily.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;31068f44-7df5-482e-8177-9a6ed914b9f7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT pg_create_logical_replication_slot(&#8217;databricks_mfg_paloalto_slot&#8217;, &#8216;pgoutput&#8217;);</code></pre></div><p>Key facts about slots:</p><ul><li><p><strong>Slots are server-wide</strong> &#8212; the name must be unique across the entire PostgreSQL instance, not just within a database.</p></li><li><p><strong>One slot per consumer</strong> &#8212; a slot can only be consumed by one connection at a time. If a second pipeline tries to use the same slot, it will be rejected.</p></li><li><p><strong>Unacknowledged WAL accumulates</strong> &#8212; if a pipeline is stopped and not consuming, the slot holds back WAL. Monitor <code>pg_replication_slots.lag</code> to avoid disk pressure.</p></li></ul><div><hr></div><h2><strong>Replication user</strong></h2><p>Create a dedicated user with replication privileges and read access to the tables being replicated:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;6677e7f2-5b27-4245-afe9-59ccbdc01902&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">CREATE USER databricks_replication WITH PASSWORD &#8216;...&#8217; REPLICATION;

-- On RDS, also grant the rds_replication role
GRANT rds_replication TO databricks_replication;

GRANT CONNECT ON DATABASE mydb TO databricks_replication;
GRANT USAGE ON SCHEMA iot1 TO databricks_replication;
GRANT SELECT ON ALL TABLES IN SCHEMA iot1 TO databricks_replication;</code></pre></div><div><hr></div><h2><strong>Replica identity</strong></h2><p>For CDC updates and deletes to include the old row values (needed to identify which row changed), each table needs a replica identity:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;427b12c5-544b-407d-b649-8e29f2ce3526&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">-- Default: uses the primary key (recommended when a PK exists)
ALTER TABLE iot1.devices REPLICA IDENTITY DEFAULT;

-- Full: includes all columns (required when there is no PK)
ALTER TABLE iot1.devices REPLICA IDENTITY FULL;</code></pre></div><div><hr></div><h2><strong>One slot per consumer, not per catalog</strong></h2><p>The Databricks docs suggest creating one replication slot per source catalog. A more precise rule is: <strong>one replication slot per consumer (ingestion pipeline)</strong>.</p><p>If you want to replicate the same database to two different workspaces, or run multiple pipelines in a test environment, each needs its own slot. Using the same slot across multiple pipelines will cause one of them to lose data.</p><div><hr></div><h2><strong>Multiple databases on the same server</strong></h2><p>PostgreSQL publications are per-database, but replication slots are server-wide. If you have two databases (<code>mfg_paloalto</code> and <code>mfg_austin</code>) on the same server, create two replication slots with different names.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;bb44048e-92a8-4b52-bde9-64c9b0165bfc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Server
&#9500;&#9472;&#9472; Database: mfg_paloalto
&#9474;   &#9492;&#9472;&#9472; Publication: mfg_paloalto_cdc_pub        (per-database)
&#9500;&#9472;&#9472; Database: mfg_austin
&#9474;   &#9492;&#9472;&#9472; Publication: mfg_austin_cdc_pub           (per-database)
&#9474;
&#9500;&#9472;&#9472; Slot: databricks_mfg_paloalto_slot             (server-wide, for mfg_paloalto DB)
&#9492;&#9472;&#9472; Slot: databricks_mfg_austin_slot               (server-wide, for mfg_austin DB)</code></pre></div><div><hr></div><h2><strong>Databricks side</strong></h2><p>Three entities need to be created in order. Below shows the json sent using the Databricks CLI.</p><h3><strong>1. Unity Catalog connection</strong></h3><p>The connection is at the <strong>server level</strong> &#8212; no database name. One connection can serve all databases on the same PostgreSQL instance.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;852b7347-c5b2-4757-81e1-e8fc30ed4cf8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">// connection.json
{
  &#8220;name&#8221;: &#8220;my-postgres-connection&#8221;,
  &#8220;connection_type&#8221;: &#8220;POSTGRESQL&#8221;,
  &#8220;options&#8221;: {
    &#8220;host&#8221;: &#8220;myinstance.abc123.us-east-1.rds.amazonaws.com&#8221;,
    &#8220;port&#8221;: &#8220;5432&#8221;,
    &#8220;user&#8221;: &#8220;databricks_replication&#8221;,
    &#8220;password&#8221;: &#8220;&lt;password&gt;&#8221;
  }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;b8da2a4c-83a7-43a9-a627-bcfa18d6e1ed&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">databricks connections create --json connection.json</code></pre></div><p>A single connection can be used to ingest from multiple databases, as long as the user has permissions.</p><h3><strong>2. Gateway pipeline</strong></h3><p>The gateway pipeline runs <strong>continuously</strong> on a classic cluster. It connects to PostgreSQL, reads the WAL via the replication slot, and buffers raw CDC events into a storage schema in Unity Catalog.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;ebf3cf64-8e7c-4aac-aa98-9a6a12cdbbe5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">// gateway.json
{
  &#8220;name&#8221;: &#8220;my-postgres-gateway&#8221;,
  &#8220;catalog&#8221;: &#8220;my_catalog&#8221;,
  &#8220;schema&#8221;: &#8220;cdc_gateway_storage&#8221;,
  &#8220;channel&#8221;: &#8220;CURRENT&#8221;,
  &#8220;continuous&#8221;: true,
  &#8220;gateway_definition&#8221;: {
    &#8220;connection_name&#8221;: &#8220;my-postgres-connection&#8221;,
    &#8220;gateway_storage_catalog&#8221;: &#8220;my_catalog&#8221;,
    &#8220;gateway_storage_schema&#8221;: &#8220;cdc_gateway_storage&#8221;
  }
  &#8220;clusters&#8221;: [
    {
      &#8220;label&#8221;: &#8220;default&#8221;,
      &#8220;driver_node_type_id&#8221;: &#8220;r5.xlarge&#8221;,
      &#8220;node_type_id&#8221;: &#8220;r5.xlarge&#8221;,
      &#8220;autoscale&#8221;: {
        &#8220;min_workers&#8221;: 1,
        &#8220;max_workers&#8221;: 5
      }        
    }
  ]
}</code></pre></div><p>Setting min_workers = max_workers = 0 will give you a driver-only cluster, if your CDC stream is low volume.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;03cd3ba8-b050-4e82-bee4-85568611f253&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">databricks pipelines create --json gateway.json</code></pre></div><p>Note the <code>pipeline_id</code> returned &#8212; it is needed for the ingestion pipeline.</p><h3><strong>3. Ingestion pipeline</strong></h3><p>The ingestion pipeline reads buffered events from the gateway and writes them as Delta tables. It runs on serverless and is <strong>triggered</strong> (not continuous).</p><p><code>source_configurations</code> maps each source database to its replication slot and publication. (Unfortunately the postgres source database is referred to as <code>catalog.source_catalog</code> which is a little confusing.) <code>objects</code> controls which schemas or tables to replicate and where they land.</p><p>The json below creates a single pipeline from two source schemas in different databases (<code>mfg_paloalto and</code> <code>mfg_austin</code>). Notice how the slot names correspond to the right database.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;8ceef6b3-a122-4fcb-a88d-3e2c758037ba&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">// ingestion.json
{
  &#8220;name&#8221;: &#8220;my-postgres-ingestion&#8221;,
  &#8220;catalog&#8221;: &#8220;my_catalog&#8221;,
  &#8220;schema&#8221;: &#8220;mfg_ingest_metadata&#8221;,
  &#8220;ingestion_definition&#8221;: {
    &#8220;ingestion_gateway_id&#8221;: &#8220;&lt;gateway-pipeline-id&gt;&#8221;,
    &#8220;source_type&#8221;: &#8220;POSTGRESQL&#8221;,
    &#8220;connection_name&#8221;: null,
    &#8220;objects&#8221;: [
      {
        &#8220;schema&#8221;: {
          &#8220;source_catalog&#8221;: &#8220;mfg_paloalto&#8221;,
          &#8220;source_schema&#8221;: &#8220;iot1&#8221;,
          &#8220;destination_catalog&#8221;: &#8220;my_catalog&#8221;,
          &#8220;destination_schema&#8221;: &#8220;mfg_paloalto_iot1&#8221;
        }
      },
      {
        &#8220;schema&#8221;: {
          &#8220;source_catalog&#8221;: &#8220;mfg_austin&#8221;,
          &#8220;source_schema&#8221;: &#8220;iot1&#8221;,
          &#8220;destination_catalog&#8221;: &#8220;my_catalog&#8221;,
          &#8220;destination_schema&#8221;: &#8220;mfg_austin_iot1&#8221;
        }
      }
    ],
    &#8220;source_configurations&#8221;: [
      {
        &#8220;catalog&#8221;: {
          &#8220;source_catalog&#8221;: &#8220;mfg_paloalto&#8221;,
          &#8220;postgres&#8221;: {
            &#8220;slot_config&#8221;: {
              &#8220;slot_name&#8221;: &#8220;databricks_mfg_paloalto_slot&#8221;,
              &#8220;publication_name&#8221;: &#8220;mfg_paloalto_cdc_pub&#8221;
            }
          }
        }
      },
      {
        &#8220;catalog&#8221;: {
          &#8220;source_catalog&#8221;: &#8220;mfg_austin&#8221;,
          &#8220;postgres&#8221;: {
            &#8220;slot_config&#8221;: {
              &#8220;slot_name&#8221;: &#8220;databricks_mfg_austin_slot&#8221;,
              &#8220;publication_name&#8221;: &#8220;mfg_austin_cdc_pub&#8221;
            }
          }
        }
      }
    ]
  }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;90560eec-f3e3-4a6c-93bf-a097991181c0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">databricks pipelines create --json ingestion.json</code></pre></div><p>The top level catalog.schema (<code>my_catalog.mfg_ingest_metadata</code> above) hold event logs and checkpoints for all the pipelines.</p><p>The above create command also returns a pipeline id <code>ingestion-pipeline-id</code>) which can be used to trigger the pipeline.</p><h3><strong>Triggering a run</strong></h3><p>After creation, trigger an initial snapshot + CDC run:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;b0dc0c33-a706-4c1a-9241-1dcd302e01cb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">databricks pipelines start-update &lt;ingestion-pipeline-id&gt;</code></pre></div><p>The gateway pipeline should run continually. The ingestion pipeline can be run at whatever interval you want, including continually.</p><div><hr></div><h2><strong>Summary checklist</strong></h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;ed027d86-4255-4919-b6db-7ae3653d4ab0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Summary checklist

Step                    Scope                        Notes
&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
wal_level = logical     Server                       Requires reboot on RDS
Replication user        Server                       One user can serve all databases
REPLICA IDENTITY        Per table                    Set before creating the publication
Publication             Per database                 One per database is typical
Replication slot        Per database, server-wide    One slot per ingestion pipeline
                        name
Network access          Server                       Allow Databricks cluster egress IP on port 5432</code></pre></div>]]></content:encoded></item><item><title><![CDATA[Ingest to Your Lakehouse Without Kafka or Kinesis | Zerobus]]></title><description><![CDATA[How Databricks Zerobus Replaces Your Message Bus With a Single Endpoint]]></description><link>https://www.databricksters.com/p/zero-infra-no-brokers-how-zerobus</link><guid isPermaLink="false">https://www.databricksters.com/p/zero-infra-no-brokers-how-zerobus</guid><dc:creator><![CDATA[Yashodhan]]></dc:creator><pubDate>Wed, 25 Feb 2026 14:04:16 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/188988485/40da827937828b2f9178293f37637727.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<h3><strong>Complex Ingestion Architecture</strong></h3><p>Today&#8217;s data teams face a common challenge: streaming data from applications to their Lakehouse requires maintaining complex infrastructure. The typical setup involves managing a message bus like Kafka, configuring connectors, monitoring pipelines, and dealing with significant operational overhead and costs&#8212;all just to move data from point A to point B.</p><h3><strong>Managed Bus Don&#8217;t Solve Everything</strong></h3><p>While <strong>Amazon Managed Streaming for Apache Kafka(Amazon MSK) </strong>removes the burden of managing servers, it doesn&#8217;t eliminate your responsibility for the message bus itself. You&#8217;re still on the hook for capacity planning, topic and partition design, producer and consumer tuning, monitoring and alerting, and upgrade timing. Managed services make these tasks less manual, but upgrades remain risky and the operational complexity persists.</p><p>Cost management is another pain point. <strong>Amazon Managed Streaming for Apache Kafka(Amazon MSK)</strong> bills can balloon quickly due to over-provisioned brokers, excess partitions, high replication factors, and long retention periods. AWS manages the infrastructure, but not your spending discipline&#8212;that&#8217;s still your problem.</p><h3><strong>What You Actually Need</strong></h3><p>A fully abstracted streaming service that eliminates cluster management entirely, letting you focus on building data products instead of babysitting message bus infrastructure.</p><h3><strong>The Challenge</strong></h3><p>A rapidly growing organization was processing massive device data volumes from Go applications. After essential first-level processing, they needed to stream data to their data lake for near real-time analytics. To avoid the complexity of managing Kafka or similar message bus infrastructure, they took a shortcut: direct writes to their data warehouse with append-only inserts.</p><p>Initially simple, this approach quickly hit walls. As volumes grew, they vertically scaled, then horizontally distributed producers across multiple warehouse instances. Small but relentless queries created network bottlenecks. Excessive delta commits from numerous producers killed throughput. They hit soft limits on connections and write operations. To keep data flowing, they over-provisioned compute&#8212;watching costs balloon without proportional gains.</p><h3><strong>The Solution</strong></h3><p>Zerobus Ingest provided the purpose-built ingestion layer they needed. Their Go applications integrated the SDK with minimal code changes&#8212;same append-only pattern, properly architected. The Write-Ahead Logging (WAL) based system handled buffering and batching, while automatic recovery managed network issues that previously caused data loss.</p><p>Data now lands directly in Delta tables, eliminating the warehouse intermediary. The result: lower latency, higher throughput, dramatically reduced costs, and one less system to manage. They got the simplicity of direct writes with the scalability of proper streaming&#8212;at a fraction of Kafka&#8217;s cost.</p><div class="pullquote"><h2><em><strong>Zerobus Ingest</strong></em></h2></div><h3><strong>Overview</strong></h3><p>Zerobus Ingest is a fully managed, zero-configuration service that enables record-by-record data ingestion directly into Delta tables. No more intermediate message buses. No more complex configurations. Just point your application at an endpoint and start sending data. The Zerobus Ingest API buffers transmitted data before adding it to a Delta table. This buffering creates an efficient and durable ingestion mechanism that supports a high volume of clients with variable throughput.</p><p><em>Before:</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eimt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eimt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 424w, https://substackcdn.com/image/fetch/$s_!eimt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 848w, https://substackcdn.com/image/fetch/$s_!eimt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 1272w, https://substackcdn.com/image/fetch/$s_!eimt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eimt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png" width="1452" height="552" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0668090b-f758-4033-855b-49012749eceb_1452x552.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:552,&quot;width&quot;:1452,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:190418,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/182907350?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!eimt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 424w, https://substackcdn.com/image/fetch/$s_!eimt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 848w, https://substackcdn.com/image/fetch/$s_!eimt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 1272w, https://substackcdn.com/image/fetch/$s_!eimt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0668090b-f758-4033-855b-49012749eceb_1452x552.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>After:</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!riic!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!riic!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 424w, https://substackcdn.com/image/fetch/$s_!riic!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 848w, https://substackcdn.com/image/fetch/$s_!riic!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 1272w, https://substackcdn.com/image/fetch/$s_!riic!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!riic!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png" width="1450" height="554" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:554,&quot;width&quot;:1450,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:166939,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/182907350?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!riic!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 424w, https://substackcdn.com/image/fetch/$s_!riic!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 848w, https://substackcdn.com/image/fetch/$s_!riic!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 1272w, https://substackcdn.com/image/fetch/$s_!riic!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a609ac-77e2-4b14-bc73-c6075b83dc3a_1450x554.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Features</strong></h3><p>Zerobus Ingest leverages a Write Ahead Log (WAL) architecture that enables it to store and acknowledge accepted records quickly, delivering low write latency for your applications. The system is backed by persistent disk storage where both the Write Ahead Log (WAL) and checkpoints are maintained, enabling several powerful capabilities:</p><p><strong>Automatic Recovery</strong> - Network issues are handled transparently by the SDK. It automatically reconnects on transient failures and resends unacknowledged records without requiring any application-level error handling code.</p><p><strong>Efficient Resource Management</strong> - Once data syncs successfully to Delta tables, Zerobus Ingest automatically cleans up Write Ahead Log (WAL) logs and metadata, freeing disk space for new data without manual intervention.</p><p><strong>Schema Management</strong> - Automatic validation against your Delta table schema catches data quality issues at ingestion time, preventing malformed data from entering your Lakehouse.</p><div class="pullquote"><h2><strong>Usage</strong></h2><h6><em><strong>Implement Zerobus</strong></em></h6></div><h3><strong>SDKs</strong></h3><p>Users will interact with Zerobus Ingest through a dedicated SDK for their language of choice. The documentation and samples are out for <a href="https://github.com/databricks/zerobus-sdk-py">Python SDK</a>, <a href="https://github.com/databricks/zerobus-sdk-rs">Rust SDK</a> and <a href="https://github.com/databricks/zerobus-sdk-java">Java SDK </a>. Both the <a href="https://github.com/databricks/zerobus-sdk-go">Go</a> and <a href="https://www.npmjs.com/package/@databricks/zerobus-ingest-sdk">TypeScript</a> SDKs for Zerobus Ingest are now publicly available. GRPC is the main communication mechanism for Zerobus Ingest.</p><p>Databricks documentation contains a <a href="https://docs.databricks.com/aws/en/ingestion/zerobus-ingest">well documented guide</a> with sample clients in multiple languages. It guides you right from installing the SDK in your preferred language to creating a Protobuf definition and a sample usage.</p><h3><strong>Supported Formats</strong></h3><ul><li><p><strong>Protocols</strong>: gRPC (primary), HTTP REST, Kafka wire format (coming soon)</p></li><li><p><strong>Data Formats</strong>: Protocol Buffers, JSON</p></li></ul><h3><strong>TIPs</strong></h3><ul><li><p>Visit the table history on UC to get a sense of how frequently the table is updated</p></li><li><p>Handle the two exceptions gracefully <em><strong>NonRetriableException, ZerobusException</strong></em>.</p></li><li><p>Even though Zerobus Ingest periodically issues data file compactions, so you don&#8217;t need to worry about the small files</p></li><li><p>Don&#8217;t forget to create a table with appropriate data types before you run the client</p></li></ul><div class="pullquote"><h2><strong>Zerobus Ingest Deep Dive</strong></h2><h6><em><strong>While the experience is simple, the engineering is sophisticated!</strong></em></h6></div><h3><strong>Components</strong></h3><ol><li><p><strong>Zerobus Ingest Server - </strong>Think of them as scalable stateful pod on K8s attached with an SSD disk(high IOPS). Its responsibilities include:</p><ul><li><p>Schema validation of the message to the table.</p></li><li><p>Materializing the data in a timely manner to the target table.</p></li><li><p>Sending an acknowledgement to the client that the data is durable.</p></li></ul></li><li><p><strong>Smart Networking and orchestration - </strong>API proxy which distributes the streams to Zerobus Ingest servers per the target delta table and scales pods as the utilization nears the roof</p></li><li><p><strong>Delta kernel -</strong> Record batch writer kicks off every 1-5 seconds, uses Delta kernel(uses Arrow) and writes the record batch to the delta table. Kicks in the PO compaction to avoid small files. <a href="https://github.com/delta-io/delta-kernel-rs">Rust APIs</a> hides all the complex details of the Delta protocol specification. Binding available for <a href="https://github.com/delta-io/delta-rs">python</a>.</p></li><li><p><strong>Write-Ahead Log(WAL) -</strong> Records are immediately persisted to durable storage(think SSD disks with high IOPS) provided by the cloud platform your databricks is running on and is acknowledged in under 50ms. This guarantees durability even if something fails</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uH8W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uH8W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 424w, https://substackcdn.com/image/fetch/$s_!uH8W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 848w, https://substackcdn.com/image/fetch/$s_!uH8W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 1272w, https://substackcdn.com/image/fetch/$s_!uH8W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uH8W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png" width="1456" height="658" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:658,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:743931,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/188988485?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uH8W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 424w, https://substackcdn.com/image/fetch/$s_!uH8W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 848w, https://substackcdn.com/image/fetch/$s_!uH8W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 1272w, https://substackcdn.com/image/fetch/$s_!uH8W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe158800b-6d97-4644-b4c8-0b80360f08d0_2886x1304.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Zerobus Ingest fits direct lakehouse writes with durable acknowledgments (no bus-style retention/multi-consumer). Zerobus Ingest it not a replacement of message bus in all scenarios. If you need message bus durability/retention or multiple subscribers, Event Hubs/Kafka is likely a safer choice.</p><h3><strong>Availability</strong></h3><p>Databricks only support single availability zone (single AZ) durability. This means Zerobus Ingest service may experience downtime. This might change soon.</p><h3><strong>Kafka still wins when</strong></h3><p>Despite the cost advantages of Zerobus Ingest Ingest, Kafka remains a better choice in following scenarios:</p><p><strong>Exactly-once semantics requirements</strong> - For financial transactions, order processing, or other workflows where duplicate processing could cause serious issues, Kafka&#8217;s exactly-once delivery guarantees are critical. While Zerobus Ingest roadmap includes this feature, organizations that need it today must still rely on Kafka.</p><p><strong>Ultra-low latency fan-out</strong> - If your use case requires multiple consumers reading the same stream with different processing logic, Kafka&#8217;s pub-sub model excels. Zerobus Ingest currently lacks the subscriber/consumer model that makes Kafka so powerful for fan-out patterns where one stream feeds multiple downstream applications.</p><h3><strong>Other Limitations</strong></h3><p>As of writing,</p><ul><li><p>Zerobus Ingest provides <strong>at-least-once delivery semantics</strong>, meaning each message will be delivered one or more times. It does not yet support <strong>exactly-once</strong> semantics. However, the duplicates can be handled using other Databricks and delta features.</p></li><li><p>Zerobus Ingest currently supports <strong>writing only to managed</strong> Delta tables</p></li><li><p><strong>Schema evolution</strong> on target tables is <strong>not yet supported</strong> in Zerobus Ingest, so the table schema must match the incoming message structure.</p></li><li><p>Each individual message is limited to a <strong>maximum size of 10 MB</strong> when processed through Zerobus Ingest.</p></li></ul><h3><strong>What&#8217;s Next</strong></h3><p>Databricks is actively enhancing Zerobus Ingest with several key features in development. The roadmap includes <strong>exactly-once delivery semantics</strong> for stronger consistency guarantees, <strong>MQTT</strong> protocol support to broaden IoT and device connectivity options, comprehensive <strong>CDC pipeline capabilities</strong> that will handle updates and deletes in addition to inserts and <strong>subscriber/consumer</strong> model to enable more flexible data consumption patterns.</p><p>Enjoy streaming in a cost efficient and simplified manner!</p><div class="pullquote"><h2><strong>Conclusion</strong></h2></div><p>Zerobus Ingest offers a compelling alternative to message bus in a lot of scenarios. While Kafka remains essential for complex streaming architectures, Zerobus Ingest closes the gap for straightforward ingestion use cases&#8212;delivering the reliability you need at a fraction of the cost and complexity.</p><p>The cost savings extend beyond infrastructure. Kafka expertise commands premium salaries, and maintaining distributed message bus systems requires dedicated engineering time that could be spent on higher-value work. Zerobus Ingest&#8217;s simplicity means junior engineers can manage what previously required highly skilled distributed systems expertise. When you factor in reduced operational overhead, lower training costs, and faster time-to-production, the economics become even more compelling. Sometimes the best architecture isn&#8217;t the most sophisticated&#8212;it&#8217;s the one that solves your problem.</p><h2>FAQ</h2><h5><strong>What is Zerobus Ingest?</strong></h5><p>A fully managed ingestion service for sending application data directly into Delta tables without operating a separate message bus.</p><h5><strong>When is Zerobus Ingest a good fit?</strong></h5><p>It works best for high-volume, append-only ingestion where the primary goal is to land data in the Lakehouse quickly and simply.</p><h5><strong>Does Zerobus Ingest replace a message bus for every use case?</strong></h5><p>No. If you need long retention, multiple downstream subscribers, or mature exactly-once semantics today, a message bus is still the better fit.</p><h5><strong>What delivery semantics does Zerobus Ingest provide today?</strong></h5><p>It currently provides <strong>at-least-once</strong> delivery, so downstream designs should account for possible duplicates.</p><h5><strong>What tables and formats are supported?</strong></h5><p>The draft states that Zerobus Ingest currently writes only to <strong>managed Delta tables</strong> and supports <strong>Protocol Buffers</strong> and <strong>JSON</strong>.</p><h5><strong>What are the main implementation requirements?</strong></h5><p>You need to create the target table in advance, align the incoming schema with the table schema, and handle SDK exceptions appropri</p><p></p><h2>Reference</h2><p><a href="https://www.databricks.com/blog/announcing-general-availability-zerobus-ingest-part-lakeflow-connect">Stream 10+ GB/sec to your lakehouse in under 5 seconds with zero infrastructure overhead</a></p><p><a href="https://community.databricks.com/t5/technical-blog/deep-dive-on-zerobus-ingest-now-ga/ba-p/148385">Deep dive on Zerobus Ingest, now GA</a></p><h2></h2>]]></content:encoded></item><item><title><![CDATA[Observability for Any Agent, Anywhere: Production-Ready Tracing with MLflow & OpenTelemetry on Databricks]]></title><description><![CDATA[MLflow OpenTelemetry traces in Unity Catalog create a continuous improvement flywheel for AI agents through analytics, evals, and monitoring.]]></description><link>https://www.databricksters.com/p/observability-for-any-agent-anywhere</link><guid isPermaLink="false">https://www.databricksters.com/p/observability-for-any-agent-anywhere</guid><dc:creator><![CDATA[Anoop Sunke]]></dc:creator><pubDate>Fri, 20 Feb 2026 16:03:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d936aaad-6c40-4be8-8c7a-0e4ae848188d_1376x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Executive Summary</h2><ul><li><p><strong>The Problem:</strong> AI agents generate massive volumes of trace data, but traditional observability tools make that data expensive to retain, difficult to govern, and hard to use in evaluation and analytics workflows.</p></li><li><p><strong>The Solution:</strong> MLflow now supports writing OpenTelemetry (OTEL) traces directly to Unity Catalog tables via a fully managed, serverless ingestion path.</p></li><li><p><strong>The Benefit: </strong>By landing traces directly in the Lakehouse, teams get governed, analytics-ready observability data with long-term retention, unified evaluation and monitoring workflows, and no OTEL infrastructure to operate.</p></li><li><p><strong>The Outcome: </strong>Production traces become immediately usable for analysis and evaluation, enabling faster iteration loops between real-world usage, model evaluation, and continuous improvement.</p></li></ul><h2>Why AI Tracing Breaks Traditional Observability</h2><p>As AI applications move into production, traces become one of the clearest ways to understand how agents actually behave by capturing prompts, tool calls, responses, latency, and execution paths. Without strong tracing, it&#8217;s hard to understand why agents behave the way they do, making debugging, evaluation, and governance much more difficult.</p><p>The challenge isn&#8217;t that observability platforms can&#8217;t ingest this data. It&#8217;s that AI traces quickly become valuable beyond debugging. Teams want to retain them longer, analyze them with SQL, join them with business and model data, and reuse them for evaluation and monitoring. When traces live only inside observability systems, that flexibility is limited, governance becomes fragmented, and moving data into analytics workflows often requires extra pipelines and duplication, especially when sensitive prompt data is involved.</p><h2>MLflow and OTEL Trace Ingestion</h2><p>Databricks now <a href="https://docs.databricks.com/aws/en/mlflow3/genai/tracing/trace-unity-catalog">supports</a> writing MLflow traces directly to Unity Catalog using the OpenTelemetry (OTEL) format. In practice, this means traces can be ingested in real time and stored in Delta tables, where they benefit from the same scalability, governance, and tooling as the rest of your data.</p><p>This changes how teams can use trace data:</p><ul><li><p><strong>Real-time ingestion with practical retention:</strong> Traces can be written as they&#8217;re generated at high throughput (GBs/sec) and retained long-term without the cost pressure typically associated with observability platforms.</p></li><li><p><strong>Analyze and govern using the Lakehouse:</strong> Once traces are tables, you can treat them like any other dataset: query them with SQL, build dashboards, run ETL pipelines, use tools like <a href="https://docs.databricks.com/aws/en/genie/">Genie</a>, and apply governance controls such as PII masking.</p></li><li><p><strong>Use the full MLflow evaluation stack:</strong> Persisting traces in Unity Catalog removes typical experiment constraints (such as <a href="https://docs.databricks.com/aws/en/resources/limits">trace caps</a>), making it easier to run large offline evaluations, monitor production systems, and continuously improve quality as workloads grow.</p></li></ul><h3>The Engineering trade-off: SaaS vs. Lakehouse</h3><p>So why not rely entirely on a SaaS observability tool?</p><ol><li><p><strong>Retention economics: </strong>Agents generate massive text payloads. Storing this data in Delta Lake on object storage is often significantly more cost-effective than SaaS-based retention models.</p></li><li><p><strong>The PII deadlock: </strong>Sending raw prompts to third-party platforms can create InfoSec friction. Keeping traces inside Unity Catalog helps maintain data sovereignty and simplifies governance.</p></li><li><p><strong>Analytics, not just telemetry:</strong> SaaS tools are strong for operational metrics like latency, but the Lakehouse gives you something different: an analytics and AI engine. You can join traces with business data &#8212; revenue, conversions, customer outcomes &#8212; to understand real impact, not just system health. Furthermore, the Lakehouse enables you to apply AI directly to your traces, allowing for advanced use cases like classifying user interactions as &#8216;good&#8217; or &#8216;bad,&#8217; and building evaluation frameworks to continuously improve system quality.</p></li></ol><h2>Architecture: Serverless OpenTelemetry Ingestion</h2><p>MLflow tracing can use the OpenTelemetry (OTEL) standard, which separates instrumentation from storage. In a typical OTEL deployment, teams are responsible for running collector fleets, scaling agents, handling backpressure, and managing reliability.</p><p>Databricks removes that operational layer by providing a managed OpenTelemetry endpoint, transparently powered by <a href="https://docs.databricks.com/aws/en/ingestion/zerobus-overview">Zerobus</a>. Zerobus is a serverless ingestion engine that enables applications to stream data directly into Delta tables using a gRPC API. Applications can easily export spans, logs, and metrics from <strong>any OTEL-compatible client</strong> directly to Unity Catalog tables, where the data is stored in Delta format.  Zerobus acts as the telemetry pipeline, handling ingestion and durability so teams don&#8217;t have to operate their own collectors.</p><p>From there, traces become first-class data in the Lakehouse, powering MLflow evaluations and monitoring, ad-hoc SQL analysis, dashboards, and downstream analytics. This creates a continuous improvement <strong>flywheel</strong> where production behavior feeds evaluation and analysis, which in turn drives faster iteration and better agent performance.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AIlN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AIlN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 424w, https://substackcdn.com/image/fetch/$s_!AIlN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 848w, https://substackcdn.com/image/fetch/$s_!AIlN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 1272w, https://substackcdn.com/image/fetch/$s_!AIlN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AIlN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png" width="1456" height="777" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:777,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3766120,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/188328490?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AIlN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 424w, https://substackcdn.com/image/fetch/$s_!AIlN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 848w, https://substackcdn.com/image/fetch/$s_!AIlN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 1272w, https://substackcdn.com/image/fetch/$s_!AIlN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba10508-cf89-4511-8266-a232bac5f7e3_1920x1025.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2><strong>Tutorial: Wiring Traces into the Lakehouse</strong></h2><h3>Sample agent: Support manager assistant</h3><p>For this blog, we&#8217;ll create a simple support manager assistant that we can use to demonstrate tracing end-to-end. The agent can be deployed outside of Databricks, as we&#8217;ve done here, highlighting that trace ingestion is decoupled from where the agent runs.</p><p>We built a LangGraph agent powered by a <a href="https://docs.databricks.com/aws/en/machine-learning/foundation-model-apis/supported-models#-anthropic-claude-sonnet-4">Databricks-hosted Claude Sonnet 4 model</a> for reasoning and response generation. The agent calls a Genie Space as a tool, which you can deploy <a href="https://www.databricks.com/resources/demos/tutorials/aibi-customer-support-review-dashboards-and-genie?itm_data=demo_center&amp;itm_source=www&amp;itm_category=resources&amp;itm_page=tutorials&amp;itm_location=Data%20Warehouse%20and%20BI&amp;itm_component=card&amp;itm_offer=aibi-customer-support-review-dashboards-and-genie">here</a>.</p><p>When a user asks a data-driven question, the agent invokes Genie through the MCP tool API. Genie translates the request into SQL, executes it against the support dataset, and returns the result. The agent then summarizes the findings and provides actionable takeaways for a support manager.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ye7I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ye7I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 424w, https://substackcdn.com/image/fetch/$s_!ye7I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 848w, https://substackcdn.com/image/fetch/$s_!ye7I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 1272w, https://substackcdn.com/image/fetch/$s_!ye7I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ye7I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png" width="667" height="111" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:111,&quot;width&quot;:667,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14975,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/188328490?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ye7I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 424w, https://substackcdn.com/image/fetch/$s_!ye7I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 848w, https://substackcdn.com/image/fetch/$s_!ye7I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 1272w, https://substackcdn.com/image/fetch/$s_!ye7I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fd4f6b-3002-49d2-bd64-dba9754731fc_667x111.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Setting up MLflow tracing with UC</h3><p>Before instrumenting the agent, we first configure MLflow to store traces in Unity Catalog. This involves creating the underlying OpenTelemetry tables and linking them to an MLflow experiment so traces can be searched, analyzed, and annotated from the UI. Start by identifying (or creating) a SQL warehouse and an MLflow experiment, then use the MLflow Python library to create the Unity Catalog tables and link the schema to the experiment. For full steps, follow the docs <a href="https://docs.databricks.com/aws/en/mlflow3/genai/tracing/trace-unity-catalog">here</a>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;db906a00-42d5-4fd1-addd-154efbb0f3dd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">import os
import mlflow
from mlflow.entities import UCSchemaLocation
from mlflow.tracing.enablement import set_experiment_trace_location

mlflow.set_tracking_uri("databricks")

os.environ["MLFLOW_TRACING_SQL_WAREHOUSE_ID"] = "&lt;warehouse-id&gt;"

experiment_name = "&lt;experiment-name&gt;"
catalog_name = "&lt;catalog&gt;"
schema_name = "&lt;schema&gt;"

experiment_id = mlflow.create_experiment(name=experiment_name)

set_experiment_trace_location(
    location=UCSchemaLocation(
        catalog_name=catalog_name,
        schema_name=schema_name,
    ),
    experiment_id=experiment_id,
)</code></pre></div><p>This setup creates Unity Catalog tables for spans, logs, and metrics. Once traces begin flowing, the MLflow service also creates Databricks views that transform the underlying OpenTelemetry data into an MLflow-friendly format for easier querying and analysis. These include:</p><ul><li><p><strong>mlflow_experiment_trace_otel_spans</strong>: detailed execution steps for each request</p></li><li><p><strong>mlflow_experiment_trace_otel_logs</strong>: structured events such as metadata, tags, and assessments</p></li><li><p><strong>mlflow_experiment_trace_otel_metrics</strong>: numerical telemetry captured during execution</p></li><li><p><strong>mlflow_experiment_trace_metadata</strong>: MLflow tags, metadata, and assessments grouped by trace ID</p></li><li><p><strong>mlflow_experiment_trace_unified</strong>: a consolidated view that assembles all trace data into a single record per trace. For better performance at scale, consider converting it to a materialized view with incremental refresh.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0mPM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0mPM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 424w, https://substackcdn.com/image/fetch/$s_!0mPM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 848w, https://substackcdn.com/image/fetch/$s_!0mPM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 1272w, https://substackcdn.com/image/fetch/$s_!0mPM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0mPM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png" width="790" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bd12fad-1472-4d95-86d6-af315e542030_790x276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:790,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0mPM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 424w, https://substackcdn.com/image/fetch/$s_!0mPM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 848w, https://substackcdn.com/image/fetch/$s_!0mPM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 1272w, https://substackcdn.com/image/fetch/$s_!0mPM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd12fad-1472-4d95-86d6-af315e542030_790x276.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>After configuring the trace destination, agent instrumentation remains the same. You can do automatic and/or manual tracing as described <a href="https://docs.databricks.com/aws/en/mlflow3/genai/tracing/app-instrumentation/">here</a>. In our example, we rely on <code>mlflow.langchain.autolog()</code> to capture the detailed LangGraph execution (model calls and tool calls). We also wrap the entrypoint with <code>@mlflow.trace</code> to establish a request-level root span, allowing each invocation to be observed as a single end-to-end execution.</p><h3>Inspecting a sample trace</h3><p>Now that the agent is instrumented and traces are flowing into Unity Catalog, let&#8217;s look at a real execution.</p><p>For this example, we asked the Support Manager Assistant:</p><blockquote><p>&#8220;Which support engineer should I put up for promotion?&#8221;</p></blockquote><p>The agent evaluated the request, called the Genie space multiple times to gather supporting data, and returned a recommendation based on performance metrics.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AcEM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AcEM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 424w, https://substackcdn.com/image/fetch/$s_!AcEM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 848w, https://substackcdn.com/image/fetch/$s_!AcEM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 1272w, https://substackcdn.com/image/fetch/$s_!AcEM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AcEM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png" width="1210" height="560" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:560,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AcEM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 424w, https://substackcdn.com/image/fetch/$s_!AcEM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 848w, https://substackcdn.com/image/fetch/$s_!AcEM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 1272w, https://substackcdn.com/image/fetch/$s_!AcEM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a476c30-e51f-4298-977e-ad5a85543aa4_1210x560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>While the response looks straightforward, the trace reveals the underlying execution path that produced it. In the MLflow experiment, we can see each of the tool calls as well as the reasoning logic of our claude sonnet model. We can see that it called the genie space tool three times before putting together a final answer.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6G7s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6G7s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 424w, https://substackcdn.com/image/fetch/$s_!6G7s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 848w, https://substackcdn.com/image/fetch/$s_!6G7s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 1272w, https://substackcdn.com/image/fetch/$s_!6G7s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6G7s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png" width="488" height="623" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:623,&quot;width&quot;:488,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6G7s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 424w, https://substackcdn.com/image/fetch/$s_!6G7s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 848w, https://substackcdn.com/image/fetch/$s_!6G7s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 1272w, https://substackcdn.com/image/fetch/$s_!6G7s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d552252-5c34-46b2-a430-cc0f8c7b7504_488x623.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We can click through each of the individual steps to study the inputs and outputs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ejSG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ejSG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 424w, https://substackcdn.com/image/fetch/$s_!ejSG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 848w, https://substackcdn.com/image/fetch/$s_!ejSG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 1272w, https://substackcdn.com/image/fetch/$s_!ejSG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ejSG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png" width="1056" height="581" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:581,&quot;width&quot;:1056,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ejSG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 424w, https://substackcdn.com/image/fetch/$s_!ejSG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 848w, https://substackcdn.com/image/fetch/$s_!ejSG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 1272w, https://substackcdn.com/image/fetch/$s_!ejSG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a107d80-9a43-49d6-8042-c0bc5fc89184_1056x581.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Because traces are stored as Delta tables, they can be queried like any other dataset. We can start with the <code>mlflow_experiment_trace_unified</code> view, where we will find a record that includes the request, response, trace metadata, and an array of the spans.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mhqL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mhqL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 424w, https://substackcdn.com/image/fetch/$s_!mhqL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 848w, https://substackcdn.com/image/fetch/$s_!mhqL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 1272w, https://substackcdn.com/image/fetch/$s_!mhqL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mhqL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png" width="779" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:779,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68062,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/188328490?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mhqL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 424w, https://substackcdn.com/image/fetch/$s_!mhqL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 848w, https://substackcdn.com/image/fetch/$s_!mhqL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 1272w, https://substackcdn.com/image/fetch/$s_!mhqL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151eb393-06db-488f-9f9f-f8552c1dc125_779x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Beyond Debugging: Analytics on Trace Data</h2><p>Now that traces are stored in Unity Catalog, they become immediately available for both batch and streaming analytics.</p><h3>Governance in Unity Catalog</h3><p>Prompts and responses, however, often contain sensitive information, so treating trace data as governed data is critical. By storing it in Unity Catalog, traces inherit fine-grained access controls, from catalog and schema permissions to column masking and row-level filtering,  enabling secure, production-ready analytics without limiting flexibility.</p><p>Once access is established, teams can securely run ad-hoc analytics by querying the underlying tables and views with SQL, as we did above. We can also build ETL pipelines, in addition to dashboards and genie spaces, for actionable business insights.</p><h3>Dashboards</h3><p>One of the most powerful aspects of having traces in Unity Catalog is that we aren&#8217;t locked into a vendor&#8217;s rigid, pre-canned views. Because the traces are in Delta tables, we can build custom dashboards that reflect our specific business logic, not just generic system health.</p><p>Using AI/BI Dashboards, we built an<strong> <a href="https://github.com/brunohub/mlflow-traces-observability/tree/main">AI Operations Center</a> </strong>that sits directly on top of our trace tables. This dashboard provides a unified view of our application performance, costs, and reliability. Instead of learning a proprietary query language, we just wrote standard SQL (with the help of <a href="https://www.databricks.com/blog/introducing-databricks-assistant-data-science-agent">AI</a>) to extract exactly what we needed.</p><p>Here are some key capabilities this unlocked:</p><p><strong>Custom Cost &amp; Token Analysis</strong> <br>Generic &#8220;cost&#8221; metrics are rarely accurate because every team negotiates different rates or uses fine-tuned models with unique pricing. Since we control the SQL, we embedded our specific pricing logic directly into the query. Our dashboard tracks token usage by model type (e.g., GPT-4o vs. Claude 4 Sonnet) and applies our contract-specific rates to calculate a precise <strong>Estimated Cost per Trace</strong>. This lets us spot expensive outliers immediately&#8212;like a single complex query that costs $0.50 due to a retrieval loop.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!czXE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!czXE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 424w, https://substackcdn.com/image/fetch/$s_!czXE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 848w, https://substackcdn.com/image/fetch/$s_!czXE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 1272w, https://substackcdn.com/image/fetch/$s_!czXE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!czXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png" width="1041" height="708" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:708,&quot;width&quot;:1041,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!czXE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 424w, https://substackcdn.com/image/fetch/$s_!czXE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 848w, https://substackcdn.com/image/fetch/$s_!czXE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 1272w, https://substackcdn.com/image/fetch/$s_!czXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F004f1220-a2ad-48cd-97a9-c8e627211d33_1041x708.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Component-Level Performance</strong></p><p>High-level latency metrics often hide the real culprit. Is the bottleneck the LLM or is it the Genie space retrieval? We built a <strong>&#8220;Tool Performance&#8221;</strong> widget that breaks down latency (P50, P99) and error rates for every individual tool in our agent (e.g., retrieve_docs vs. generate_response). This allows us to pinpoint exactly which step in the chain is degrading the user experience.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lJfx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lJfx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 424w, https://substackcdn.com/image/fetch/$s_!lJfx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 848w, https://substackcdn.com/image/fetch/$s_!lJfx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 1272w, https://substackcdn.com/image/fetch/$s_!lJfx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lJfx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png" width="1310" height="718" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:718,&quot;width&quot;:1310,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lJfx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 424w, https://substackcdn.com/image/fetch/$s_!lJfx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 848w, https://substackcdn.com/image/fetch/$s_!lJfx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 1272w, https://substackcdn.com/image/fetch/$s_!lJfx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89f860e1-86b0-4285-8c96-cfc77d290f24_1310x718.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Genie spaces</h3><p>Both business and technical stakeholders often want to explore agent behavior without writing SQL. By exposing trace tables through Genie, teams can enable natural-language analysis over their telemetry data, allowing users to ask questions about performance, tool usage, latency, and model behavior directly. In our example, this could include questions such as:</p><ul><li><p>What types of requests require escalation?</p></li><li><p>Are tool retries increasing?</p></li><li><p>Which queries trigger the most complex execution paths?</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xlNf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xlNf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 424w, https://substackcdn.com/image/fetch/$s_!xlNf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 848w, https://substackcdn.com/image/fetch/$s_!xlNf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 1272w, https://substackcdn.com/image/fetch/$s_!xlNf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xlNf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png" width="920" height="437" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:437,&quot;width&quot;:920,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xlNf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 424w, https://substackcdn.com/image/fetch/$s_!xlNf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 848w, https://substackcdn.com/image/fetch/$s_!xlNf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 1272w, https://substackcdn.com/image/fetch/$s_!xlNf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d855164-9878-4f7a-90c5-3540ab887ef9_920x437.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>ETL pipelines</h3><p>Because traces are stored as Delta tables, they can feed downstream ETL pipelines just like any other dataset. By enabling <a href="https://docs.databricks.com/aws/en/delta/delta-change-data-feed">Change Data Feed (CDF)</a>, teams can process trace data incrementally, either in batch or streaming, without repeatedly scanning entire tables.</p><p>This makes it possible to operationalize observability. For example, a pipeline could monitor trace patterns and trigger alerts when latency exceeds defined thresholds, tool failures spike, or token usage deviates from expected baselines. These signals can then feed dashboards, notification systems, or automated remediation workflows.</p><p>Importantly, this complements real-time protections such as <a href="https://docs.databricks.com/aws/en/ai-gateway/overview-serving-endpoints#ai-guardrails">AI Guardrails</a>. While guardrails enforce policy at request time, ETL pipelines create a feedback loop, helping teams analyze trends, refine policies, and continuously improve agent performance.</p><p></p><h2>Closing the Loop: From Production Traces to Evaluation</h2><p>Once traces are available, they can power the full MLflow 3 <a href="https://docs.databricks.com/aws/en/mlflow3/genai/eval-monitor/">evaluation stack</a>, enabling teams to measure, improve, and maintain the quality of their AI applications across the entire lifecycle. Evaluation and monitoring build directly on tracing, allowing the same telemetry captured during development, testing, and production to be scored using LLM judges and custom metrics.</p><h3>Evaluate during development using AI Judges</h3><p>MLflow allows us to run evaluations against an evaluation dataset, applying built-in or custom judges to score response quality. One effective approach is to bootstrap this dataset from real traces. Because these prompts originate from actual user interactions, they better represent the scenarios your agent must handle compared to synthetic test cases.</p><p>Below, we create an evaluation dataset from recently captured traces. MLflow uses a SQL warehouse to search and materialize dataset records, so be sure to configure the warehouse ID in your environment.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;bc030680-8dd0-46fd-8220-29d726db3488&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import os
import mlflow
import mlflow.genai.datasets
import time

# Required for dataset operations
os.environ["MLFLOW_TRACING_SQL_WAREHOUSE_ID"] = MLFLOW_TRACING_SQL_WAREHOUSE_ID

DATASET_NAME = f"{CATALOG_NAME}.{SCHEMA_NAME}.support_management_chatbot_traces"

# Create (or load) the dataset
try:
    eval_dataset = mlflow.genai.datasets.create_dataset(name=DATASET_NAME)
except Exception:
    eval_dataset = mlflow.genai.get_dataset(name=DATASET_NAME)

# Pull recent traces (example - from yesterday)
yesterday = int((time.time() - 60 * 60 * 24) * 1000)

traces_df = mlflow.search_traces(
    filter_string=f"attributes.timestamp_ms &gt; {yesterday}",
    order_by=["attributes.timestamp_ms DESC"],
)

# Merge traces into the dataset
eval_dataset = eval_dataset.merge_records(traces_df[["inputs"]])</code></pre></div><p>With the dataset in place, we can define the judges that will score our application. MLflow provides a set of built-in judges, and also allows us to define custom guidelines tailored to our agent&#8217;s expected behavior.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;542a1e40-6a26-40e3-a001-a638c4f625fc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">from mlflow.genai.scorers import RelevanceToQuery, Safety, Guidelines

# Define judges
agent_judges = [
    RelevanceToQuery(),
    Guidelines(
        name="analytical_correctness",
        guidelines="The response must correctly interpret the data and avoid unsupported conclusions.",
    ),
    Guidelines(
        name="actionable_support_insights",
        guidelines="The response must provide at least one concrete, data-backed recommendation.",
    ),
    Guidelines(
        name="performance_management",
        guidelines="The response should not recommend admonishing or firing employees.",
    ),
    Safety(),
]

# Run evaluation
eval_results = mlflow.genai.evaluate(
    data=eval_dataset,
    predict_fn=predict_fn,
    scorers=agent_judges,
)

eval_results</code></pre></div><p>And we can now see the results in the MLflow experiment.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JMne!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JMne!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 424w, https://substackcdn.com/image/fetch/$s_!JMne!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 848w, https://substackcdn.com/image/fetch/$s_!JMne!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 1272w, https://substackcdn.com/image/fetch/$s_!JMne!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JMne!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png" width="1332" height="319" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:319,&quot;width&quot;:1332,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69873,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.databricksters.com/i/188328490?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JMne!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 424w, https://substackcdn.com/image/fetch/$s_!JMne!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 848w, https://substackcdn.com/image/fetch/$s_!JMne!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 1272w, https://substackcdn.com/image/fetch/$s_!JMne!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd855155-6d0d-4355-91aa-bddada2a1bdc_1332x319.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Production monitoring</h3><p>Development evaluations help us validate behavior before release, but production monitoring shows us how the application performs with real users. MLflow can automatically evaluate live traces using the same judges, helping us quickly detect regressions, drift, and emerging failure patterns. This turns evaluation from a one-time task into an ongoing practice as the application evolves.</p><p></p><h2>Frequently Asked Questions (FAQ)</h2><ul><li><p><strong>Can I use this for agents running outside of Databricks?</strong></p><p>Yes, the agent can be running anywhere. In fact the support assistant agent example that was used for this blog is deployed locally.</p></li><li><p><strong>What are the throughput and storage limits of this solution?</strong></p><p>The ingestion throughput <a href="https://docs.databricks.com/aws/en/mlflow3/genai/tracing/trace-unity-catalog#-limitations">limit is 200 QPS</a> today. There is no limit on storage. Previous limits on traces per experiment are no longer applicable. If you need higher throughput limits, please reach out to your Databricks account team.</p></li><li><p><strong>What can I do to ensure my search queries, MLflow experiment experience, and downstream analytics remain performant?</strong></p><p>Consider optimizing the OTEL tables using Z-ordering as described <a href="https://docs.databricks.com/aws/en/mlflow3/genai/tracing/observe-with-traces/query-dbsql#performance-considerations">here</a>.</p></li><li><p><strong>How does this handle PII found in user prompts?</strong></p><p>This feature does not apply any special handling to PII. However, the data is stored in Unity Catalog, where you can leverage governance capabilities, such as fine-grained access controls, column masking, and row filtering, to manage and restrict downstream access.</p></li></ul><p></p><h2>Get started</h2><p>To get started, follow along with the <a href="https://docs.databricks.com/aws/en/mlflow3/genai/tracing/trace-unity-catalog">documentation</a>.</p>]]></content:encoded></item><item><title><![CDATA[Genie Integration with Google Chat]]></title><description><![CDATA[Talk to your data from Google Chat - powered by Databricks Genie!]]></description><link>https://www.databricksters.com/p/genie-integration-with-google-chat</link><guid isPermaLink="false">https://www.databricksters.com/p/genie-integration-with-google-chat</guid><dc:creator><![CDATA[Ambarish]]></dc:creator><pubDate>Tue, 10 Feb 2026 16:03:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_uxC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_uxC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_uxC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!_uxC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!_uxC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!_uxC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_uxC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_uxC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!_uxC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!_uxC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!_uxC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40949259-0752-4094-8d2c-0afb55d160ed_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1><strong>Integrate Databricks Genie with Google Chat in 30 mins!</strong></h1><p>With this integration, your team can query business data directly from Google Chat using natural language. No SQL knowledge needed, no switching between tools.</p><h2><strong>Features</strong></h2><ul><li><p><strong>Conversational AI in Google Chat</strong> - Ask questions like &#8220;What were total sales last quarter?&#8221; and get instant, data-backed answers</p></li><li><p><strong>Rich Card Responses</strong> - Results are displayed as formatted Google Chat Cards with data tables, generated SQL</p></li><li><p><strong>Response Feedback Loop</strong> - Thumbs up/down buttons send ratings back to Genie so space authors can review and improve</p></li><li><p><strong>Direct Messages and @Mentions</strong> - Works in 1:1 DMs with the bot or by @mentioning it in any Chat Space</p></li><li><p><strong>No Infrastructure Required</strong> - Runs entirely on Google Apps Script (serverless), no servers to manage, no Databricks App to deploy</p></li><li><p><strong>Secure by Default</strong> - Credentials stored in Apps Script Properties Service, data governed by Unity Catalog</p></li></ul><h2><strong>Architecture</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CiOu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CiOu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 424w, https://substackcdn.com/image/fetch/$s_!CiOu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 848w, https://substackcdn.com/image/fetch/$s_!CiOu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 1272w, https://substackcdn.com/image/fetch/$s_!CiOu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CiOu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png" width="1011" height="339" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:339,&quot;width&quot;:1011,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CiOu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 424w, https://substackcdn.com/image/fetch/$s_!CiOu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 848w, https://substackcdn.com/image/fetch/$s_!CiOu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 1272w, https://substackcdn.com/image/fetch/$s_!CiOu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff33307ed-dbc7-47a6-acc5-d69bbfbfa887_1011x339.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Prerequisites</strong></h2><ol><li><p><strong>Databricks Workspace</strong></p><ul><li><p>Active Databricks workspace</p></li><li><p>Genie space ID</p></li></ul></li><li><p><strong>Google Workspace with the Enterprise plan</strong></p><ul><li><p>Enable Google Chat API in the project</p></li><li><p>Enable App Script in the same project</p></li></ul></li></ol><h2><strong>Setup Instructions</strong></h2><p><strong>Set up Genie Space in Databricks Workspace and note the few parameters</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h4>Genie Details</h4><p>If you already have a Genie Space, skip to the next step. Otherwise:</p><ol><li><p>In your Databricks workspace, navigate to <strong>Genie</strong> in the left sidebar</p></li><li><p>Click <strong>New</strong> in the upper-right corner of the screen</p></li><li><p>Add your data sources. Then, click <strong>Create</strong></p></li><li><p>From the <strong>Settings</strong> tab, note the <strong>Space ID</strong> (a 32-character string) -- you will need this later</p></li></ol><blockquote><p><strong>Tip:</strong> The quality of your Genie Space directly affects the quality of your answers. Add company-specific context, test with expected user questions, and iterate on your table annotations.</p></blockquote><h4>Generate a Databricks Token</h4><blockquote><p>Apps Script needs a token to authenticate with the Genie API.</p></blockquote><ol><li><p>In your Databricks workspace, click your username (top right) then <strong>Settings</strong></p></li><li><p>Go to <strong>Developer</strong> then <strong>Access Tokens</strong></p></li><li><p>Click <strong>Generate New Token</strong>, give it a description like &#8220;Google Chat Genie Bot&#8221;</p></li><li><p><strong>Copy the token immediately</strong> -- you will not see it again</p></li></ol><blockquote><p>Also note your <strong>workspace URL</strong> (e.g., https://your-instance.cloud.databricks.com).</p></blockquote><p><strong>For production:</strong> Use OAuth M2M with a service principal instead of a personal access token for better security and automated token rotation.</p><h3><strong>Google Workspace Setup</strong></h3><h4>Google Cloud Project Setup</h4><ol><li><p>Go to the <strong>Google Cloud Console</strong> (console.cloud.google.com)</p></li><li><p>Create a new project (or select an existing one)</p></li><li><p>Navigate to <strong>APIs and Services</strong> then <strong>Library</strong></p></li><li><p>Search for <strong>Google Chat API</strong> and click <strong>Enable</strong></p></li><li><p>Go to <strong>APIs and Services</strong> then <strong>OAuth consent screen</strong></p></li><li><p>Select <strong>Internal</strong>, fill in the app name (e.g., &#8220;Genie Data Bot&#8221;), and save</p></li></ol><h4>Create the Apps Script Project</h4><ol><li><p>Go to <strong>script.google.com</strong> and click <strong>New project</strong></p></li><li><p>Rename the project to <strong>Genie Chat Bot</strong></p></li><li><p>Download all files from the repo <a href="https://github.com/adgitdemo/ad_databricks/tree/main/genie-google-chat-app">https://github.com/adgitdemo/ad_databricks/tree/main/genie-google-chat-app</a>  and create the corresponding .gs files in the Apps Script editor:</p></li></ol><ul><li><p><strong>Code.gs</strong> -- Chat event handlers (onMessage, onAddedToSpace, submitFeedback)</p></li><li><p><strong>appscript.json </strong>- App Script configuration</p></li><li><p>(Recommended) Go to <strong>Project Settings</strong> (gear icon) then under the &#8220;<strong>Script Properties</strong>&#8220; section, click Add script property (or Edit script properties):</p><ul><li><p>DATABRICKS_TOKEN = your token from Step 1</p></li><li><p>GENIE_SPACE_ID = your space ID from Step 1</p></li><li><p>DATABRICKS_HOST = your workspace URL (no trailing slash)</p></li></ul></li><li><p>The code reads from Script Properties automatically.</p></li></ul><h4>Configure the Google Chat API</h4><ol><li><p>In Apps Script, click <strong>Deploy-&gt;New Deployment, </strong>select<strong> Add-On </strong>configuration, and deploy. Copy the <strong>Deployment ID </strong>once process completed.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GrCk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GrCk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 424w, https://substackcdn.com/image/fetch/$s_!GrCk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 848w, https://substackcdn.com/image/fetch/$s_!GrCk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 1272w, https://substackcdn.com/image/fetch/$s_!GrCk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GrCk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png" width="1456" height="1143" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2042c1f-1024-4938-a367-12965751174e_1496x1174.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1143,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GrCk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 424w, https://substackcdn.com/image/fetch/$s_!GrCk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 848w, https://substackcdn.com/image/fetch/$s_!GrCk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 1272w, https://substackcdn.com/image/fetch/$s_!GrCk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2042c1f-1024-4938-a367-12965751174e_1496x1174.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nBd1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nBd1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 424w, https://substackcdn.com/image/fetch/$s_!nBd1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 848w, https://substackcdn.com/image/fetch/$s_!nBd1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 1272w, https://substackcdn.com/image/fetch/$s_!nBd1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nBd1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png" width="1098" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1098,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nBd1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 424w, https://substackcdn.com/image/fetch/$s_!nBd1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 848w, https://substackcdn.com/image/fetch/$s_!nBd1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 1272w, https://substackcdn.com/image/fetch/$s_!nBd1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1e5698-52a8-4aea-816b-d7b166cd7e19_1098x788.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ol start="2"><li><p>Back in the <strong>Google Cloud Console</strong>, go to <strong>APIs and Services</strong> then <strong>Google Chat API</strong> then <strong>Configuration</strong></p></li><li><p>Fill in:</p></li></ol><blockquote><p>   - <strong>App name:</strong> db-genie-1</p><p>   - <strong>Avatar URL:</strong> (optional -- your company logo or Databricks icon)</p><p>   - <strong>Description:</strong> Databricks Genie Bot</p><p>   - <strong>Functionality:</strong> Check &#8220;Join spaces and group conversations&#8221;</p><p>   - <strong>Connection settings:</strong> Select <strong>Apps Script</strong> and paste your Head Deployment ID in the <strong>Deployment ID </strong>field</p><p>   - <strong>Triggers </strong>specify the apps script functions to handle interactions</p><p>   - <strong>Visibility:</strong> Select &#8220;Specific people and groups&#8221; and add your team or test users</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CGrX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CGrX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 424w, https://substackcdn.com/image/fetch/$s_!CGrX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 848w, https://substackcdn.com/image/fetch/$s_!CGrX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 1272w, https://substackcdn.com/image/fetch/$s_!CGrX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CGrX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png" width="1104" height="1338" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1338,&quot;width&quot;:1104,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CGrX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 424w, https://substackcdn.com/image/fetch/$s_!CGrX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 848w, https://substackcdn.com/image/fetch/$s_!CGrX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 1272w, https://substackcdn.com/image/fetch/$s_!CGrX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcaf5dd3-515a-4972-b0ae-9eb3aaf4fb4b_1104x1338.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KeI3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KeI3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 424w, https://substackcdn.com/image/fetch/$s_!KeI3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 848w, https://substackcdn.com/image/fetch/$s_!KeI3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 1272w, https://substackcdn.com/image/fetch/$s_!KeI3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KeI3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png" width="1112" height="1390" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1390,&quot;width&quot;:1112,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KeI3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 424w, https://substackcdn.com/image/fetch/$s_!KeI3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 848w, https://substackcdn.com/image/fetch/$s_!KeI3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 1272w, https://substackcdn.com/image/fetch/$s_!KeI3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35efd478-7c4b-4549-8437-905d5f98aa79_1112x1390.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Add Chat App/Bot App to Google Chat</h4><p>1. Open the Space</p><blockquote><p>Go into the Space where you want the app.</p></blockquote><p>2. Click the Space name at the top</p><blockquote><p>A menu opens.</p></blockquote><p>3. Choose &#8220;Manage apps&#8221;</p><blockquote><p>Then click Add apps.</p></blockquote><p>4. Search + Add the app</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n8tR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n8tR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 424w, https://substackcdn.com/image/fetch/$s_!n8tR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 848w, https://substackcdn.com/image/fetch/$s_!n8tR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 1272w, https://substackcdn.com/image/fetch/$s_!n8tR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n8tR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png" width="715" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:715,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n8tR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 424w, https://substackcdn.com/image/fetch/$s_!n8tR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 848w, https://substackcdn.com/image/fetch/$s_!n8tR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 1272w, https://substackcdn.com/image/fetch/$s_!n8tR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46348ae2-ceb1-4c68-8d9d-9f54a6aedc38_715x153.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2><strong>Usage</strong></h2><p>Once deployed, your team can interact with the bot in two ways:</p><p><strong>Direct Messages</strong> -- Open a DM with Genie Data Bot and type your question directly. Great for ad-hoc data exploration.</p><p><strong>@Mentions in Spaces</strong> -- In any Google Chat Space where the bot is added, type @db-genie-1 followed by your question. Responses appear in the space, and anyone can ask follow-ups in the thread.</p><h2><strong>Demo Time</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pWah!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pWah!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 424w, https://substackcdn.com/image/fetch/$s_!pWah!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 848w, https://substackcdn.com/image/fetch/$s_!pWah!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 1272w, https://substackcdn.com/image/fetch/$s_!pWah!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pWah!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png" width="1456" height="700" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:700,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pWah!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 424w, https://substackcdn.com/image/fetch/$s_!pWah!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 848w, https://substackcdn.com/image/fetch/$s_!pWah!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 1272w, https://substackcdn.com/image/fetch/$s_!pWah!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d73461-fee1-4f0f-a608-d781c66b6020_1600x769.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NujH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NujH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 424w, https://substackcdn.com/image/fetch/$s_!NujH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 848w, https://substackcdn.com/image/fetch/$s_!NujH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 1272w, https://substackcdn.com/image/fetch/$s_!NujH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NujH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png" width="1307" height="843" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:843,&quot;width&quot;:1307,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NujH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 424w, https://substackcdn.com/image/fetch/$s_!NujH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 848w, https://substackcdn.com/image/fetch/$s_!NujH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 1272w, https://substackcdn.com/image/fetch/$s_!NujH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76614994-e3bb-493e-8215-a0828fa16a30_1307x843.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Tips and Considerations</strong></h2><ul><li><p><strong>Answer quality depends on your Genie Space.</strong> Add detailed table/column descriptions, sample SQL, and company-specific context for best results.</p></li><li><p><strong>Sync timeout:</strong> Apps Script has a 30-second synchronous response limit for Chat. If your Genie queries take longer, consider an optional async pattern using time-driven triggers and the Advanced Chat Service.</p></li><li><p><strong>Rate limits:</strong> The Genie API allows approximately 5 queries per minute per workspace during Public Preview.</p></li><li><p><strong>Row limits:</strong> Genie returns up to 5,000 rows per query. The Chat card displays up to 20 rows for readability, with a link to view full results in Genie.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Migrating Existing Dashboards to Databricks AI/BI, Part 1: Context and Cascading Filters]]></title><description><![CDATA[How to implement context filters and &#8220;only relevant values&#8221; behavior in Databricks AI/BI Dashboards]]></description><link>https://www.databricksters.com/p/migrating-existing-dashboards-to</link><guid isPermaLink="false">https://www.databricksters.com/p/migrating-existing-dashboards-to</guid><dc:creator><![CDATA[Artem Chebotko]]></dc:creator><pubDate>Tue, 03 Feb 2026 18:02:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!72WV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!72WV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!72WV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!72WV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!72WV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!72WV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!72WV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/289c7692-0274-4274-a173-9db55df49c08_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!72WV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!72WV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!72WV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!72WV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F289c7692-0274-4274-a173-9db55df49c08_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As a Specialist Solutions Architect at Databricks, I regularly work with customers who are migrating critical analytics from existing BI tools to Databricks AI/BI Dashboards &#8211; and the first questions I usually get are about filters.</p><p><strong>Teams want to know</strong>:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p><em>&#8220;What&#8217;s the Databricks equivalent of the context filters we use today?&#8221;</em></p></li><li><p><em>&#8220;Can we still do cascading filters where each dropdown only shows relevant values?&#8221;</em></p></li><li><p><em>&#8220;Do you support filter actions when I click on a bar or a point?&#8221;</em></p></li><li><p><em>&#8220;How do we do user-based filtering in AI/BI Dashboards?&#8221;</em></p></li></ul><p>These aren&#8217;t cosmetic features. They&#8217;re how analysts actually interact with dashboards, and they&#8217;re often the reason an existing BI dashboard feels &#8220;alive&#8221; instead of static.</p><p>In this post, I&#8217;ll walk through how to implement two familiar filter patterns from existing BI dashboards in Databricks AI/BI Dashboards, using the built-in <code>samples.tpch</code> dataset:</p><ol><li><p><strong>Context filters</strong> &#8594; implemented as parameters in dataset SQL</p></li><li><p><strong>&#8220;</strong><em><strong>Only Relevant Values</strong></em><strong>&#8221; or cascading filters</strong> &#8594; implemented with field filters and query-based parameters</p></li></ol><p>Row-level security and user-based filtering deserve their own deep dive, and action-style interactions (cross-filtering and drill-through) could easily fill another post, so I&#8217;ll cover those separately.</p><p>I&#8217;ve also published the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, so you can follow along and inspect the configurations yourself.</p><h3><strong>Quick primer: datasets and filters in Databricks AI/BI Dashboards</strong></h3><p>Before we map those patterns, it helps to align on a few AI/BI Dashboards concepts:</p><h4><strong>Datasets</strong></h4><p>In AI/BI Dashboards, each dashboard has a <em>Data</em> tab where you define one or more datasets:</p><ul><li><p>A dataset is defined by an SQL query, direct reference to a Unity Catalog table/view, or an uploaded file.</p></li><li><p>Multiple visualizations can reuse the same dataset.</p></li><li><p>Datasets are bundled with the dashboard when you share/export/import it.</p></li></ul><p>Practically, a dataset is your &#8220;model&#8221; for a set of visuals: one query, many charts.</p><h4><strong>Field filters vs parameter filters</strong></h4><p>AI/BI Dashboards support two core ways to filter data from a dashboard: <a href="https://docs.databricks.com/aws/en/dashboards/filters#should-i-filter-on-a-field-or-a-parameter">field filters and parameter filters</a>. Both are implemented as <strong>filter widgets</strong>, but they behave differently under the hood.</p><p><strong>Field filters</strong> are applied directly to dataset fields (columns) on top of the dataset query. Processing behaviour is defined by the <a href="https://docs.databricks.com/aws/en/dashboards/caching#dataset-optimizations">dataset performance thresholds</a>. Specifically, for small datasets (&#8804; 100K rows or &#8804; 100MB), results are pulled to the browser and visualization-specific filtering and aggregation are applied client-side. For larger datasets, Databricks wraps the dataset query in a <code>WITH</code> clause and applies the filter predicates and aggregations in Databricks SQL warehouse (DBSQL).</p><p><strong>Parameter filters </strong>are applied to parameters, which are variables that get substituted into your dataset SQL at runtime. When the parameter value changes, the query is always re-run in DBSQL.</p><p>In other words, field filters operate on the results of the dataset query, while parameter filters operate inside the dataset SQL itself.</p><p>To speed up processing, various <a href="https://docs.databricks.com/aws/en/dashboards/caching#caching-and-data-freshness">caching layers</a> in AI/BI Dashboards and DBSQL are used.</p><p>We&#8217;ll use parameter filters to emulate context filters, and field filters + query-based parameters to emulate &#8220;<em>Only Relevant Values</em>.&#8221;</p><h4><strong>Filter scope: global, page-level, and widget-level</strong></h4><p>Filters in AI/BI Dashboards also differ by <a href="https://docs.databricks.com/aws/en/dashboards/filters#filter-interactivity-and-scope">scope</a>:</p><ul><li><p><strong>Global filters</strong> are interactive filters in the global filters panel that apply across all pages of the dashboard to any visualization that shares the selected datasets.</p></li><li><p><strong>Page-level filters</strong> are interactive filter widgets placed on a specific page in the canvas. They apply to all visualizations on that page that share one or more datasets.</p></li><li><p><strong>Widget-level filters</strong> are static filters configured directly on a single visualization widget in its configuration panel. Authors set the values, and viewers can&#8217;t change them.</p></li></ul><p>With that foundation in place, we can now map these context filters and &#8220;<em>Only Relevant Values</em>&#8221; patterns into AI/BI Dashboards patterns.</p><h3><strong>Sample dataset: TPCH on Databricks</strong></h3><p>To keep examples concrete, we&#8217;ll use the TPCH sample data that ships with Databricks in the <code>samples.tpch</code> schema.</p><p>For the purposes of this post, you can start by creating a dataset that joins tables <code>region</code>, <code>nation</code>, <code>customer</code>, <code>orders</code>, and <code>lineitem</code>:</p><pre><code><code>SELECT
  r.r_name              AS region,
  n.n_name              AS nation,
  c.c_custkey           AS customer_id,
  c.c_name              AS customer_name,
  o.o_orderkey          AS order_id,
  o.o_orderdate         AS order_date,
  l.l_extendedprice * (1 - l.l_discount) AS revenue
FROM samples.tpch.region   AS r
JOIN samples.tpch.nation   AS n ON n.n_regionkey = r.r_regionkey
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
JOIN samples.tpch.orders   AS o ON o.o_custkey   = c.c_custkey
JOIN samples.tpch.lineitem AS l ON l.l_orderkey  = o.o_orderkey;</code></code></pre><p>In AI/BI Dashboards, you define this query as a dataset in the <em>Data</em> tab and then reuse it across multiple visualizations. Let&#8217;s call this dataset <em>TPCH Sales</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Li34!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Li34!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 424w, https://substackcdn.com/image/fetch/$s_!Li34!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 848w, https://substackcdn.com/image/fetch/$s_!Li34!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 1272w, https://substackcdn.com/image/fetch/$s_!Li34!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Li34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png" width="1456" height="678" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:678,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Li34!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 424w, https://substackcdn.com/image/fetch/$s_!Li34!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 848w, https://substackcdn.com/image/fetch/$s_!Li34!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 1272w, https://substackcdn.com/image/fetch/$s_!Li34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2aa1025-9a71-4c07-9bd2-7315bbc81448_1600x745.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We&#8217;ll reuse this same dataset or its derivatives throughout the rest of the post to illustrate context filters and cascading filters.</p><h3><strong>Implementing context filters with parameters in dataset SQL</strong></h3><h4><strong>What a context filter does</strong></h4><p>A context filter defines a high-level subset of the data:</p><ul><li><p>The context filter is applied first, often materializing a temporary subset.</p></li><li><p>Other filters and some calculations are then evaluated on top of that subset.</p></li></ul><p>Context filters are used to:</p><ul><li><p>Improve performance by filtering early and shrinking the working set.</p></li><li><p>Enforce logical order, such as &#8220;<em>always filter by Region first</em>.&#8221;</p></li><li><p>Make other filters depend on that subset.</p></li></ul><h4><strong>How to think about context in AI/BI Dashboards</strong></h4><p>Given the primer:</p><ul><li><p><a href="https://docs.databricks.com/aws/en/dashboards/filters#should-i-filter-on-a-field-or-a-parameter">Field filters</a> operate on the results of the dataset query (Databricks wraps your dataset SQL and applies them on top).</p></li><li><p><a href="https://docs.databricks.com/aws/en/dashboards/filters#should-i-filter-on-a-field-or-a-parameter">Parameter filters</a> substitute values directly into your dataset SQL, so they filter inside the query, before joins and aggregations.</p></li></ul><p>If you want &#8220;context&#8221; behavior &#8211; <em>filter first, then apply everything else</em> &#8211; you should implement that filter as a <a href="https://docs.databricks.com/aws/en/dashboards/parameters">parameter</a> in the dataset SQL, driven by a parameter filter widget.</p><h4><strong>Pattern: treat the context as a base parameter</strong></h4><p>Let&#8217;s add a context filter for <em>Region</em>:</p><p>If you&#8217;re following along with the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, this setup lives on the &#8220;Context filter&#8221; page.</p><p><strong>Step 1</strong>. Define <em>TPCH Sales (Context)</em> with a <em>Region</em> parameter</p><p>Create a dataset <em>TPCH Sales (Context)</em>:</p><pre><code><code>SELECT
  r.r_name              AS region,
  n.n_name              AS nation,
  c.c_custkey           AS customer_id,
  c.c_name              AS customer_name,
  o.o_orderkey          AS order_id,
  o.o_orderdate         AS order_date,
  l.l_extendedprice * (1 - l.l_discount) AS revenue
FROM samples.tpch.region   AS r
JOIN samples.tpch.nation   AS n ON n.n_regionkey = r.r_regionkey
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
JOIN samples.tpch.orders   AS o ON o.o_custkey   = c.c_custkey
JOIN samples.tpch.lineitem AS l ON l.l_orderkey  = o.o_orderkey
WHERE r.r_name = :region_param      -- &#8220;context&#8221; filter</code></code></pre><p>In the dataset&#8217;s <em>Parameters</em> panel:</p><ul><li><p>Define <code>region_param</code> with type <em>String</em>.</p></li><li><p>Optionally set a default (for example, <em>AMERICA</em>) so the dataset runs without any dashboard filter.</p></li></ul><p>This makes <code>region_param</code> the context for all visuals that use <em>TPCH Sales (Context)</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Id06!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Id06!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 424w, https://substackcdn.com/image/fetch/$s_!Id06!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 848w, https://substackcdn.com/image/fetch/$s_!Id06!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 1272w, https://substackcdn.com/image/fetch/$s_!Id06!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Id06!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png" width="1456" height="715" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:715,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Id06!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 424w, https://substackcdn.com/image/fetch/$s_!Id06!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 848w, https://substackcdn.com/image/fetch/$s_!Id06!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 1272w, https://substackcdn.com/image/fetch/$s_!Id06!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce877d63-410b-4f3f-878b-aec639cfc9c9_1600x786.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Step 2</strong>. (Optional but nice) Create a helper dataset for <em>Region</em> values</p><p>You can drive <code>region_param</code> directly from <em>TPCH Sales (Context)</em>, but a tiny helper dataset keeps things tidy, convenient, and re-usable.</p><p>Create <em>TPCH Regions (Context)</em>:</p><pre><code><code>SELECT DISTINCT r_name AS region
FROM samples.tpch.region
ORDER BY region;</code></code></pre><p>This dataset has no parameters; it just returns the list of available regions.</p><p><strong>Step 3</strong>. Add a <em>Region</em> parameter filter widget</p><p>We will configure the widget as a page-level filter (alternatively, you can move it into the global filters panel if it should apply across pages).</p><p>On the page where you want <em>Region</em> as a context filter:</p><ol><li><p>Add a filter widget and title it <em>Region</em>.</p></li><li><p>Set the filter type to <em>Single value</em>.</p></li><li><p>Configure it as a parameter filter:</p><ul><li><p>Fields: <code>TPCH Regions (Context).region</code></p></li><li><p>Parameters: <code>TPCH Sales (Context).region_param</code></p></li></ul></li></ol><p>If you don&#8217;t want a helper dataset, you can instead use <code>TPCH Sales (Context).region</code> as the field source, but the wiring is otherwise identical.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oUxR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oUxR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 424w, https://substackcdn.com/image/fetch/$s_!oUxR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 848w, https://substackcdn.com/image/fetch/$s_!oUxR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 1272w, https://substackcdn.com/image/fetch/$s_!oUxR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oUxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png" width="512" height="641" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:641,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!oUxR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 424w, https://substackcdn.com/image/fetch/$s_!oUxR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 848w, https://substackcdn.com/image/fetch/$s_!oUxR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 1272w, https://substackcdn.com/image/fetch/$s_!oUxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b617a8-188e-4c89-84cc-0ca1ed8c84dc_512x641.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Effect</strong></p><ul><li><p>When a viewer selects <em>Region: EUROPE</em> in the <em>Region</em> filter:</p><ul><li><p>The widget writes &#8220;<em>EUROPE</em>&#8220; into <code>region_param</code> for <em>TPCH Sales (Context)</em>.</p></li><li><p><em>TPCH Sales (Context)</em> reruns with <code>WHERE r.r_name = &#8216;EUROPE&#8217;</code>.</p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hjO5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hjO5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 424w, https://substackcdn.com/image/fetch/$s_!hjO5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 848w, https://substackcdn.com/image/fetch/$s_!hjO5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 1272w, https://substackcdn.com/image/fetch/$s_!hjO5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hjO5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png" width="1237" height="626" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:626,&quot;width&quot;:1237,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!hjO5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 424w, https://substackcdn.com/image/fetch/$s_!hjO5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 848w, https://substackcdn.com/image/fetch/$s_!hjO5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 1272w, https://substackcdn.com/image/fetch/$s_!hjO5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10638dc8-f318-443c-ad39-31ed2b15c8ec_1237x626.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>All visuals built on <em>TPCH Sales (Context)</em> now use only European data as their starting point.</p></li><li><p>Any additional field filters (for example, Nation, Customer, Date) operate on this already-filtered subset, just like secondary filters evaluated after a context filter.</p></li><li><p>From the viewer&#8217;s perspective, <em>Region</em> behaves like a true context filter: it defines the base subset of data first, and everything else &#8211; other filters, cross-filtering, drill-through &#8211; is evaluated on top of that context.</p></li></ul><h3><strong>Implementing &#8220;</strong><em><strong>Only Relevant Values</strong></em><strong>&#8221; with cascading filters and query-based parameters</strong></h3><h4><strong>What &#8220;</strong><em><strong>Only Relevant Values</strong></em><strong>&#8221; behavior means</strong></h4><p>&#8220;<em>Only Relevant Values</em>&#8221; behavior on a filter shrinks the list of values based on the current state of other filters and the view:</p><ul><li><p>If you select <em>Region: ASIA</em>, the <em>Country</em> filter only shows countries that actually have data in <em>ASIA</em>.</p></li><li><p>As you add more filters, each filter&#8217;s domain is recomputed from the filtered dataset.</p></li></ul><p>Practically, this gives you cascading filters that stay in sync with each other and with the current slice of data.</p><h4><strong>How to think about &#8220;</strong><em><strong>Only Relevant Values</strong></em><strong>&#8221; in AI/BI Dashboards</strong></h4><p>In AI/BI Dashboards, you get the same effect in two ways:</p><ol><li><p><a href="https://docs.databricks.com/aws/en/dashboards/filters">Field filters</a> on the same dataset &#8211; AI/BI recomputes the value list based on the current filtered dataset.</p></li><li><p><a href="https://docs.databricks.com/aws/en/dashboards/parameters#query-based-parameters">Query-based parameters</a> &#8211; a specialized filter widget that both populates its values from a query, and writes the selected value into a parameter used in your dataset SQL.</p></li></ol><h4><strong>Pattern 1: Cascading filters with field filters</strong></h4><p>The simplest way to mimic &#8220;<em>Only Relevant Values</em>&#8221; is to use <a href="https://docs.databricks.com/aws/en/dashboards/filters">field filters</a> wired to the same dataset. AI/BI Dashboards will automatically recompute each filter&#8217;s value list based on the current filtered dataset.</p><p>We&#8217;ll build a <em>Region &#8594; Nation &#8594; Customer</em> cascade on top of <em>TPCH Sales (Cascading Pattern 1)</em>.</p><p>In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, this pattern is implemented on the &#8220;<em>Cascading filters with field filters</em>&#8221; page.</p><p><strong>Step 1</strong>. Define <em>TPCH Sales (Cascading Pattern 1)</em></p><p>Create a dataset <em>TPCH Sales (Cascading Pattern 1)</em> with the base TPCH join and a revenue metric:</p><pre><code><code>SELECT
  r.r_name              AS region,
  n.n_name              AS nation,
  c.c_custkey           AS customer_id,
  c.c_name              AS customer_name,
  o.o_orderkey          AS order_id,
  o.o_orderdate         AS order_date,
  l.l_extendedprice * (1 - l.l_discount) AS revenue
FROM samples.tpch.region   AS r
JOIN samples.tpch.nation   AS n ON n.n_regionkey = r.r_regionkey
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
JOIN samples.tpch.orders   AS o ON o.o_custkey   = c.c_custkey
JOIN samples.tpch.lineitem AS l ON l.l_orderkey  = o.o_orderkey;</code></code></pre><p>This dataset has no parameters; all filtering will be done with field filters on top of the query results.</p><p><strong>Step 2</strong>. Add <em>Region</em>, <em>Nation</em>, and <em>Customer</em> field filters</p><p>On the dashboard page where you want cascading behavior:</p><ol><li><p>Add three field filter widgets with titles <em>Region</em>, <em>Nation</em>, and <em>Customer</em>.</p></li><li><p>Configure each widget as a page-level filter (or move them into the global filters panel if you want them to apply across pages).</p></li><li><p>Connect the filters to the following fields from <em>TPCH Sales (Cascading Pattern 1)</em>:</p><ul><li><p><em>Region</em> &#8594; <code>region</code></p></li><li><p><em>Nation</em> &#8594; <code>nation</code></p></li><li><p><em>Customer</em> &#8594; <code>customer_id</code></p></li></ul></li></ol><p>No parameters are involved here &#8211; these are pure field filters on a single dataset.</p><p><strong>Effect</strong></p><ul><li><p>When a viewer selects <em>region: ASIA</em>, the <em>TPCH Sales (Cascading Pattern 1)</em> dataset is filtered to <em>ASIA</em> for all visuals on the page.</p></li><li><p>The <em>Nation</em> field filter&#8217;s value list is recomputed from that filtered dataset, so it only shows nations in <em>ASIA</em>.</p></li></ul><blockquote></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bTX3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bTX3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 424w, https://substackcdn.com/image/fetch/$s_!bTX3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 848w, https://substackcdn.com/image/fetch/$s_!bTX3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 1272w, https://substackcdn.com/image/fetch/$s_!bTX3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bTX3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png" width="1456" height="532" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:532,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!bTX3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 424w, https://substackcdn.com/image/fetch/$s_!bTX3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 848w, https://substackcdn.com/image/fetch/$s_!bTX3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 1272w, https://substackcdn.com/image/fetch/$s_!bTX3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7936f7d-2ea4-4089-928b-d1002a4b757e_1600x585.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>After the viewer chooses a nation (e.g., <em>JAPAN</em>), the <em>Customer</em> field filter shrinks to show only customers in that nation.</p></li></ul><blockquote></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D7pG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D7pG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 424w, https://substackcdn.com/image/fetch/$s_!D7pG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 848w, https://substackcdn.com/image/fetch/$s_!D7pG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 1272w, https://substackcdn.com/image/fetch/$s_!D7pG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D7pG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png" width="1456" height="531" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:531,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!D7pG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 424w, https://substackcdn.com/image/fetch/$s_!D7pG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 848w, https://substackcdn.com/image/fetch/$s_!D7pG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 1272w, https://substackcdn.com/image/fetch/$s_!D7pG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3caeb4b3-84dc-4cc5-ae15-5add326ae0f6_1600x583.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>From the user&#8217;s perspective, these field filters behave like filters with an &#8220;<em>Only Relevant Values</em>&#8221; option enabled: each dropdown shows only values that exist in the currently filtered data. Under the hood, AI/BI Dashboards are simply applying field filters on top of a single dataset and recomputing the dropdown values from the currently filtered result set.</p></li></ul><h4><strong>Pattern 2: Cascading filters with query-based parameters</strong></h4><p>In Pattern 1, we used field filters only. In some cases you may want more control over how dropdown values are loaded, or you may want the same parameter to drive multiple datasets. In that case you can use <a href="https://docs.databricks.com/aws/en/dashboards/parameters#query-based-parameters">query-based parameters</a>. A query-based parameter filter widget gets its dropdown values from a field in a &#8220;choices&#8221; dataset, and writes the selected value into one or more parameters that are used in dataset SQL.</p><p>Here we&#8217;ll build a three-level cascade <em>Region &#8594; Nation &#8594; Customer</em> using:</p><ul><li><p>One main dataset: <em>TPCH Sales (Cascading Pattern 2)</em></p></li><li><p>Three small &#8220;value list&#8221; datasets:</p><ul><li><p><em>TPCH Regions (Cascading Pattern 2)</em></p></li><li><p><em>TPCH Nations by Region (Cascading Pattern 2)</em></p></li><li><p><em>TPCH Customers by Nation (Cascading Pattern 2)</em></p></li></ul></li></ul><p>In the <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a>, this pattern is implemented on the &#8220;<em>Cascading filters with query-based parameters</em>&#8221; page.</p><p><strong>Step 1</strong>. Define <em>TPCH Sales (Cascading Pattern 2)</em></p><p>Create the <em>TPCH Sales (Cascading Pattern 2)</em> dataset with parameters for <em>region</em>, <em>nation</em>, and <em>customer</em>:</p><pre><code><code>SELECT
 r.r_name              AS region,
 n.n_name              AS nation,
 c.c_custkey           AS customer_id,
 c.c_name              AS customer_name,
 o.o_orderkey          AS order_id,
 o.o_orderdate         AS order_date,
 l.l_extendedprice * (1 - l.l_discount) AS revenue
FROM samples.tpch.region   AS r
JOIN samples.tpch.nation   AS n ON n.n_regionkey = r.r_regionkey
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
JOIN samples.tpch.orders   AS o ON o.o_custkey   = c.c_custkey
JOIN samples.tpch.lineitem AS l ON l.l_orderkey  = o.o_orderkey
WHERE (:region_param   = 'All' OR r.r_name = :region_param)
  AND (:nation_param   = 'All' OR n.n_name = :nation_param)
  AND (:customer_param = 0     OR c.c_custkey  = :customer_param);</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ft3p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ft3p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 424w, https://substackcdn.com/image/fetch/$s_!ft3p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 848w, https://substackcdn.com/image/fetch/$s_!ft3p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 1272w, https://substackcdn.com/image/fetch/$s_!ft3p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ft3p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png" width="1456" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ft3p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 424w, https://substackcdn.com/image/fetch/$s_!ft3p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 848w, https://substackcdn.com/image/fetch/$s_!ft3p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 1272w, https://substackcdn.com/image/fetch/$s_!ft3p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b3da122-46ff-4505-b268-de0d80f55758_1600x866.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the dataset&#8217;s <em>Parameters</em> panel:</p><ul><li><p>Set <code>region_param</code> type to <em>String</em>.</p></li><li><p>Set <code>nation_param</code> type to <em>String</em>.</p></li><li><p>Set <code>customer_param</code> type to <em>Numeric / Integer</em> (to match <code>c_custkey</code>).</p></li></ul><p>This last bit is important: the <em>Customer</em> filter uses a numeric field, so the parameter must be numeric as well.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qzZ2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qzZ2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 424w, https://substackcdn.com/image/fetch/$s_!qzZ2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 848w, https://substackcdn.com/image/fetch/$s_!qzZ2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 1272w, https://substackcdn.com/image/fetch/$s_!qzZ2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qzZ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png" width="262" height="297" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:297,&quot;width&quot;:262,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!qzZ2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 424w, https://substackcdn.com/image/fetch/$s_!qzZ2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 848w, https://substackcdn.com/image/fetch/$s_!qzZ2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 1272w, https://substackcdn.com/image/fetch/$s_!qzZ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96df94a4-c73f-4c97-b2bd-b8bb0ba8a63d_262x297.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Step 2</strong>. Create helper datasets for the dropdowns</p><p>1. <em>TPCH Regions (Cascading Pattern 2)</em> &#8211; list of regions:</p><pre><code><code>SELECT DISTINCT r_name AS region
FROM samples.tpch.region
ORDER BY region;</code></code></pre><p>2. <em>TPCH Nations by Region (Cascading Pattern 2)</em> &#8211; nations for the selected region:</p><pre><code><code>SELECT DISTINCT n.n_name AS nation
FROM samples.tpch.nation   AS n
JOIN samples.tpch.region   AS r ON n.n_regionkey = r.r_regionkey
WHERE r.r_name = :region_param
ORDER BY nation;</code></code></pre><p>This dataset defines its own <code>region_param</code> (<em>string</em>) in the <em>Data</em> tab.</p><p>3. <em>TPCH Customers by Nation (Cascading Pattern 2)</em> &#8211; customers for the selected nation:</p><pre><code><code>SELECT DISTINCT
  c.c_custkey AS customer_id
FROM samples.tpch.nation   AS n
JOIN samples.tpch.customer AS c ON c.c_nationkey = n.n_nationkey
WHERE n.n_name = :nation_param
ORDER BY customer_id;</code></code></pre><p>This dataset defines <code>nation_param</code> (<em>string</em>). <code>customer_id</code> is <em>numeric</em>, matching <code>customer_param</code> in <em>TPCH Sales (Cascading Pattern 2)</em>.</p><p>Run each dataset in the <em>Data</em> tab once to confirm they succeed.</p><p><strong>Step 3</strong>. Add <em>Region</em>, <em>Nation</em>, and <em>Customer</em> filter widgets</p><p>On your dashboard page, add three filter widgets and wire them to fields and parameters. Configure all three widgets as page-level filters (or move them into the global filters panel if they should apply across pages).</p><p>1. <em>Region filter widget</em></p><ul><li><p>Filter type: <em>Single value</em></p></li><li><p>Fields: <code>TPCH Regions (Cascading Pattern 2).region</code></p></li><li><p>Parameters:</p><ul><li><p><code>TPCH Sales (Cascading Pattern 2).region_param</code></p></li><li><p><code>TPCH Nations by Region (Cascading Pattern 2).region_param</code></p></li></ul></li><li><p>Default value: <code>All</code></p></li></ul><p>This keeps region_param in <em>TPCH Sales (Cascading Pattern 2)</em> and <em>TPCH Nations by Region (Cascading Pattern 2)</em> in sync.</p><p>2. <em>Nation filter widget</em></p><ul><li><p>Filter type: <em>Single value</em></p></li><li><p>Fields: <code>TPCH Nations by Region (Cascading Pattern 2).nation</code></p></li><li><p>Parameters:</p><ul><li><p><code>TPCH Sales (Cascading Pattern 2).nation_param</code></p></li><li><p><code>TPCH Customers by Nation (Cascading Pattern 2).nation_param</code></p></li></ul></li></ul><ul><li><p>Default value: <code>All</code></p></li></ul><p>This keeps nation_param in <em>TPCH Sales (Cascading Pattern 2)</em> and <em>TPCH Customers by Nation (Cascading Pattern 2)</em> in sync.</p><p>3. <em>Customer filter widget</em></p><ul><li><p>Filter type: <em>Single value</em></p></li><li><p>Fields: <code>TPCH Customers by Nation (Cascading Pattern 2).customer_id</code></p></li><li><p>Parameters: <code>TPCH Sales (Cascading Pattern 2).customer_param</code></p></li></ul><ul><li><p>Default value: <code>0</code></p></li></ul><p><strong>Effect</strong></p><ul><li><p>When a viewer selects <em>Region: AMERICA</em>:</p><ul><li><p>The <em>Region</em> widget writes &#8220;<em>AMERICA</em>&#8220; into <code>region_param</code> in <em>TPCH Sales (Cascading Pattern 2)</em> and <em>TPCH Nations by Region (Cascading Pattern 2)</em>.</p></li><li><p><em>TPCH Nations by Region (Cascading Pattern 2)</em> reruns and returns only nations in <em>AMERICA</em>, so the <em>Nation</em> dropdown only shows those nations.</p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jskP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jskP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 424w, https://substackcdn.com/image/fetch/$s_!jskP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 848w, https://substackcdn.com/image/fetch/$s_!jskP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 1272w, https://substackcdn.com/image/fetch/$s_!jskP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jskP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png" width="1456" height="531" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:531,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!jskP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 424w, https://substackcdn.com/image/fetch/$s_!jskP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 848w, https://substackcdn.com/image/fetch/$s_!jskP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 1272w, https://substackcdn.com/image/fetch/$s_!jskP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd0107b4-e71a-47c4-aecf-e1dd374b6d3d_1600x584.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>When the viewer then selects <em>Nation: UNITED STATES</em>:</p><ul><li><p>The <em>Nation</em> widget writes &#8220;<em>UNITED STATES</em>&#8220; into <code>nation_param</code> in <em>TPCH Sales (Cascading Pattern 2)</em> and <em>TPCH Customers by Nation (Cascading Pattern 2)</em>.</p></li><li><p><em>TPCH Customers by Nation (Cascading Pattern 2)</em> reruns and returns only customers in <em>UNITED STATES</em>, so the <em>Customer</em> dropdown only shows those customer IDs.</p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0lIb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0lIb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 424w, https://substackcdn.com/image/fetch/$s_!0lIb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 848w, https://substackcdn.com/image/fetch/$s_!0lIb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 1272w, https://substackcdn.com/image/fetch/$s_!0lIb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0lIb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png" width="1456" height="531" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d23de116-d555-497e-9eaf-8375f56465de_1600x583.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:531,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!0lIb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 424w, https://substackcdn.com/image/fetch/$s_!0lIb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 848w, https://substackcdn.com/image/fetch/$s_!0lIb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 1272w, https://substackcdn.com/image/fetch/$s_!0lIb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd23de116-d555-497e-9eaf-8375f56465de_1600x583.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>When the viewer selects a specific <em>Customer</em> (for example, <em>607</em>):</p><ul><li><p>The <em>Customer</em> widget writes <em>607</em> into <code>customer_param</code> in <em>TPCH Sales (Cascading Pattern 2)</em>.</p></li><li><p><em>TPCH Sales (Cascading Pattern 2)</em> reruns with <code>region_param</code>, <code>nation_param</code>, and <code>customer_param</code> applied, and all visuals built on this dataset show only orders for customer <em>607</em> in <em>UNITED STATES / AMERICA</em>.</p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Gv_s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Gv_s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 424w, https://substackcdn.com/image/fetch/$s_!Gv_s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 848w, https://substackcdn.com/image/fetch/$s_!Gv_s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 1272w, https://substackcdn.com/image/fetch/$s_!Gv_s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Gv_s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png" width="1456" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Gv_s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 424w, https://substackcdn.com/image/fetch/$s_!Gv_s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 848w, https://substackcdn.com/image/fetch/$s_!Gv_s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 1272w, https://substackcdn.com/image/fetch/$s_!Gv_s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80e03bea-826e-4d5a-9022-ad7ec61f30d2_1600x614.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>From the viewer&#8217;s perspective, <em>Region &#8594; Nation &#8594; Customer</em> behaves like cascading filters with &#8220;<em>Only Relevant Values</em>&#8221; behavior enabled. Under the hood, each dropdown is a query-based parameter filter, and the <em>Region</em> and <em>Nation</em> widgets keep parameters in multiple datasets in sync, while Customer filters the main <em>TPCH Sales (Cascading Pattern 2)</em> dataset down to a single customer.</p></li></ul><h4><strong>Which pattern when?</strong></h4><p>Both patterns get you &#8220;<em>Only Relevant Values</em>&#8221;-style cascading behavior, but they shine in different situations.</p><p><strong>Pattern 1 &#8211; Cascading field filters</strong></p><p>Use this when:</p><ul><li><p>You&#8217;re working off one main dataset per page.</p></li><li><p>You want the simplest authoring experience: add field filters, connect them to the dataset, done.</p></li><li><p>&#8220;<em>Allow All</em>&#8221; and easy clearing of filters are important to your users.</p></li></ul><p>This is the closest to what many BI tools do by default and is usually the right starting point.</p><p><strong>Pattern 2 &#8211; Cascading query-based parameters</strong></p><p>Use this when:</p><ul><li><p>You need parameters that drive multiple datasets.</p></li><li><p>You want tighter control over dropdown values, including custom queries per level.</p></li><li><p>You&#8217;re comfortable managing parameter types and wiring filters to multiple datasets.</p></li></ul><p>Pattern 2 is more flexible and explicit, but also more advanced. In practice, I start with Pattern 1 for most dashboards, and reach for Pattern 2 when I need parameter-driven logic or want to reuse the same parameters across several datasets and pages.</p><h3><strong>Summary</strong></h3><p>In this post, we looked at how to carry two of the most important filter patterns from traditional BI dashboards into Databricks AI/BI Dashboards:</p><ul><li><p><strong>Context filters</strong> become parameters in your dataset SQL, driven by parameter filter widgets. This lets you enforce &#8220;filter by Region first&#8221; semantics and shrink the working set before joins and aggregations.</p></li><li><p><strong>&#8220;Only Relevant Values&#8221; / cascading filters</strong> can be implemented either with simple field filters on a single dataset (Pattern 1) or with query-based parameters and helper datasets (Pattern 2) when you need more control and reusable parameters.</p></li></ul><p>The <a href="https://github.com/ArtemChebotko/Migrating-Existing-Dashboards-to-Databricks-AI-BI">companion dashboard</a> includes all three examples: a context filter page, a field-based cascading page, and a query-based cascading page. You can import it into your workspace and adapt the patterns to your own datasets.</p><p>In future posts, I plan to cover:</p><ul><li><p>Row-level security and user-based filtering in AI/BI Dashboards</p></li><li><p>Action-style interactions such as cross-filtering and drill-through in AI/BI Dashboards</p></li></ul><p>If you&#8217;re starting a migration from an existing BI tool to Databricks AI/BI today, I recommend:</p><ol><li><p>Identify your key context filters (Region, Business Unit, etc.) and implement them as parameters in dataset SQL.</p></li><li><p>Start with Pattern 1 (field filters) for cascading behavior, and only move to Pattern 2 where you truly need parameter-driven logic or shared parameters across datasets.</p></li></ol><p>These two patterns alone are usually enough to make an AI/BI dashboard feel as interactive and &#8220;alive&#8221; as the dashboards your teams are used to today.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.databricksters.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Databricksters! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>