<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Krishna Tangudu</title>
    <description>The latest articles on DEV Community by Krishna Tangudu (@swaroop_krishna_e2f4b83b2).</description>
    <link>https://dev.to/swaroop_krishna_e2f4b83b2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3814074%2F3d7f150a-da32-48c6-9fda-dd6f74b170e2.jpeg</url>
      <title>DEV Community: Krishna Tangudu</title>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swaroop_krishna_e2f4b83b2"/>
    <language>en</language>
    <item>
      <title>The Replication Worked. The Cutover Didn’t. — Snowflake to Fabric, Part 4</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Wed, 11 Nov 2026 17:34:26 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/the-replication-worked-the-cutover-didnt-snowflake-to-fabric-part-4-353d</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/the-replication-worked-the-cutover-didnt-snowflake-to-fabric-part-4-353d</guid>
      <description>&lt;p&gt;&lt;em&gt;Snowflake AWS → Azure → Microsoft Fabric — Gotchas, Part 4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wanted to see whether scheduled Snowflake replication could give us more control than continuous mirroring. The source changed only a few times a day, so it felt reasonable to try matching replication to those updates.&lt;/p&gt;

&lt;p&gt;Initially, things looked good. The data reached Azure, Fabric queries worked, and the row counts we compared matched.&lt;/p&gt;

&lt;p&gt;The trouble started when we connected the workspaces that people actually used. The shared lakehouse could read a table, but a downstream workspace was looking for files it could not find.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/snowflake-iceberg-fabric-what-we-tried-what-support-confirmed-and-what-comes-next-ie0"&gt;Part 2&lt;/a&gt;, I said I would try the Azure replication route. This post covers what worked, where we got stuck, and why I wasn't ready to call it a replacement for mirroring.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Architecture labels are anonymized. This is an engineering investigation, not a controlled benchmark.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I wanted to try this
&lt;/h2&gt;

&lt;p&gt;Our source updated approximately three times daily. With continuous mirroring, we had seen higher Snowflake cloud-services usage, but there was no configurable mirroring window to line up with those updates.&lt;/p&gt;

&lt;p&gt;I wanted to try this flow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snowflake on AWS → scheduled replication to Azure → Fabric access to the replicated Iceberg files.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Snowflake supports cross-cloud replication. We scheduled three refreshes daily, and the initial load and the scheduled refreshes we checked completed successfully. &lt;a href="https://docs.snowflake.com/en/user-guide/account-replication-intro" rel="noopener noreferrer"&gt;Snowflake replication overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We checked schemas, ran SQL queries and compared row counts for selected tables. Those checks passed. The next step was to connect the existing downstream workspaces.&lt;/p&gt;

&lt;p&gt;I was hoping this would help with cost too, but we hadn't measured enough to say it was cheaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shared lakehouse worked. The downstream one didn't.
&lt;/h2&gt;

&lt;p&gt;Our setup had a shared Fabric lakehouse, with other workspaces accessing its tables through shortcuts. A shortcut points to data elsewhere, so each workspace doesn't need its own copy.&lt;/p&gt;

&lt;p&gt;We changed selected shortcuts in the shared lakehouse to point to the replicated source. We kept the table names, and the downstream shortcuts continued pointing to the same shared lakehouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fev8hs9u1j3xeakf2ce6a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fev8hs9u1j3xeakf2ce6a.gif" alt="Azure replication trial, downstream shortcut failures and recovery to the mirror route." width="759" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The animation shows the routes we tested. Replication copies data from AWS to Azure; the shortcut links provide access to it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is where we started seeing SQL I/O errors and Spark missing-file errors downstream, even though reads in the shared layer worked. Some conversion logs also showed success during the investigation.&lt;/p&gt;

&lt;p&gt;There is an extra step here that is easy to overlook. Iceberg metadata describes which files belong to a table. Fabric creates virtual Delta metadata so its engines can read that Iceberg table. Completing the Snowflake refresh doesn't verify the rest of that path. &lt;a href="https://learn.microsoft.com/en-us/fabric/onelake/onelake-iceberg-tables" rel="noopener noreferrer"&gt;OneLake Iceberg virtualization&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had checked the shared layer. I needed to go one workspace farther and check the queries people would actually run.&lt;/p&gt;

&lt;h2&gt;
  
  
  We tried refreshing metadata, then rolled back
&lt;/h2&gt;

&lt;p&gt;We refreshed SQL endpoint metadata for the affected tables. Some reads recovered; others still failed after the refresh reported success.&lt;/p&gt;

&lt;p&gt;We also checked Spark separately. In Part 2, disabling the vectorized reader had helped with a decimal compatibility issue. It didn't fix these missing-file errors, so we couldn't treat this as the same problem.&lt;/p&gt;

&lt;p&gt;For affected workloads, we pointed the shared shortcuts back to their original mirrored sources. Even that didn't immediately recover every table. Some shortcut objects needed to be recreated, followed by scoped metadata refreshes.&lt;/p&gt;

&lt;p&gt;After that, the SQL and Spark sample reads we checked passed through both layers. We hadn't deleted the underlying source data. We had recovered those reads, but we still hadn't made the replica-backed route reliable enough to replace mirroring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then we found the regional restriction
&lt;/h2&gt;

&lt;p&gt;Our Azure replica and the consuming Fabric workspace were in different regions. During troubleshooting, we found a restriction in Microsoft's documentation: for Iceberg virtualization through a shortcut pointing to a &lt;strong&gt;OneLake location&lt;/strong&gt;, the target must be in the same region as the shortcut. &lt;a href="https://learn.microsoft.com/en-us/fabric/onelake/onelake-iceberg-tables#availability-and-platform-limitations" rel="noopener noreferrer"&gt;Microsoft's platform limitations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We created a fresh same-region test pointing directly to the same replica folders. Its initial conversions and all-column SQL sample reads passed. The older cross-region setup still had mixed conversion results.&lt;/p&gt;

&lt;p&gt;That was useful progress, but I wouldn't say it explained every earlier failure. The new setup had only passed its initial checks, and the older setup had worked initially too. We still needed support to explain the downstream file-reference failures.&lt;/p&gt;

&lt;p&gt;One thing I would check much earlier next time is the region of every OneLake target. Adding another shortcut doesn't move the data into a different region.&lt;/p&gt;

&lt;h2&gt;
  
  
  A query passing still wasn't the full answer
&lt;/h2&gt;

&lt;p&gt;Later, we also saw SQL samples working while the latest conversion logs showed failures. These were generic conversion errors, separate from the earlier missing-file errors and &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/when-zero-copy-and-mirroring-clash-an-iceberg-metadata-gotcha-part-3-de3"&gt;Part 3's clone-metadata issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Microsoft documents that failed conversion can leave the last successfully converted version available. So a working query alone couldn't tell us whether the latest source change had reached Fabric. &lt;a href="https://learn.microsoft.com/en-us/fabric/onelake/onelake-iceberg-tables#how-conversion-issues-surface" rel="noopener noreferrer"&gt;Conversion behavior&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I now keep these checks separate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What I want to know&lt;/th&gt;
&lt;th&gt;What I check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Did replication finish?&lt;/td&gt;
&lt;td&gt;The replica refresh result.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can downstream users read it?&lt;/td&gt;
&lt;td&gt;SQL and Spark through their actual access path.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is the expected change there?&lt;/td&gt;
&lt;td&gt;A known source change checked at the final consumer.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The row counts and previews were useful. They just weren't enough to approve the switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we are now
&lt;/h2&gt;

&lt;p&gt;Snowflake replication worked in this trial. The full Fabric consumption path still had issues, and we hadn't established a cost advantage.&lt;/p&gt;

&lt;p&gt;We are now separately testing an AWS replica with direct S3 access from Fabric. I will cover that in another post once we have a clearer result, including what happens across repeated refreshes and how the costs compare.&lt;/p&gt;

&lt;p&gt;For now, my advice is simple: before changing the source behind a shared shortcut, check the downstream workspace too. Keeping the same table name doesn't mean the queries behind it will keep working.&lt;/p&gt;

&lt;p&gt;Previous gotchas: &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/what-i-learned-running-snowflake-iceberg-mirroring-in-microsoft-fabric-and-debugging-real-1151"&gt;Part 1&lt;/a&gt; · &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/snowflake-iceberg-fabric-what-we-tried-what-support-confirmed-and-what-comes-next-ie0"&gt;Part 2&lt;/a&gt; · &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/when-zero-copy-and-mirroring-clash-an-iceberg-metadata-gotcha-part-3-de3"&gt;Part 3&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>microsoftfabric</category>
      <category>iceberg</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>Salesforce to Snowflake, With Less Friction: Zero-Copy V2 in Practice</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Wed, 28 Oct 2026 11:09:14 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/salesforce-to-snowflake-with-less-friction-zero-copy-v2-in-practice-4435</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/salesforce-to-snowflake-with-less-friction-zero-copy-v2-in-practice-4435</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/part-1-zero-copy-sharing-salesforce-snowflake-for-analytics-4f0j"&gt;Part 1&lt;/a&gt;, I described connecting Salesforce data to Snowflake. It worked, but the credential setup and Salesforce-style names added housekeeping before analysts could use it.&lt;/p&gt;

&lt;p&gt;I have since tested the V2 connector. The enrollment-code handoff is simpler, authentication uses OIDC, and friendly names remove some SQL cleanup. We also kept legacy schema aliases and compared grants before and after migration to preserve existing access.&lt;/p&gt;

&lt;p&gt;Here is the setup, what improved, and where my incremental test stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The handoff is much simpler
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Snowflake admin                  Salesforce admin
Create connector
Get Enrollment ID -------------&amp;gt; Create Snowflake V2 target
                                 Link selected Data Shares
Mount shared data &amp;lt;------------- Shares become available
Query with SQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;V2 uses OpenID Connect (OIDC). For administrators, the enrollment-code flow replaces the manual credential configuration in my original walkthrough. &lt;a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_cdp_2026_summer_snowflake_v2.htm&amp;amp;language=en_US&amp;amp;type=5" rel="noopener noreferrer"&gt;Salesforce V2 release notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source is &lt;strong&gt;Salesforce Data Cloud, now called Data 360&lt;/strong&gt;. CRM data must already be available there. Snowflake exposes it through a catalog-linked database without replicating the shared data into native Snowflake tables. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/about-salesforce-datacloud" rel="noopener noreferrer"&gt;How it works&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create the connector and get the code
&lt;/h2&gt;

&lt;p&gt;I used SQL. The example below follows that flow with generic names, using an existing administration schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;ZEROCOPY&lt;/span&gt; &lt;span class="n"&gt;CONNECTOR&lt;/span&gt;
  &lt;span class="n"&gt;PLATFORM_ADMIN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMINISTRATION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SALESFORCE_ZC&lt;/span&gt;
  &lt;span class="n"&gt;PARTNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SALESFORCE'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;SYSTEM&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GET_ZEROCOPY_CONNECTOR_CONFIG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'PLATFORM_ADMIN.ADMINISTRATION.SALESFORCE_ZC'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned configuration contains &lt;code&gt;enrollment_code&lt;/code&gt;—the value the UI calls the Enrollment ID. Pass that value securely to your Salesforce administrator. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/salesforce/setup" rel="noopener noreferrer"&gt;Snowflake connector setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also create the connector through &lt;strong&gt;Ingestion → Zero-Copy → Overview&lt;/strong&gt; in Snowsight. Choose &lt;strong&gt;Connect&lt;/strong&gt; on the Salesforce card; after creation, the setup panel displays the same Enrollment ID.&lt;/p&gt;

&lt;p&gt;Your role needs the relevant database/schema access and connector privileges. In particular, creating the connector requires &lt;code&gt;CREATE ZEROCOPY CONNECTOR&lt;/code&gt; on its schema; retrieving its configuration requires &lt;code&gt;OPERATE&lt;/code&gt;. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/salesforce/security" rel="noopener noreferrer"&gt;Privileges&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Choose what Salesforce shares
&lt;/h2&gt;

&lt;p&gt;The Salesforce administrator needs the &lt;strong&gt;Data Cloud Architect&lt;/strong&gt; permission set and V2 enabled for the org.&lt;/p&gt;

&lt;p&gt;In Salesforce:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Data Share Targets&lt;/strong&gt;, create a target, and choose &lt;strong&gt;Snowflake V2&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter its label and paste the Enrollment ID. Save it.&lt;/li&gt;
&lt;li&gt;Create or open a &lt;strong&gt;Data Share&lt;/strong&gt;, selecting the Data 360 objects to expose and reviewing its consent/access settings.&lt;/li&gt;
&lt;li&gt;On that share, choose &lt;strong&gt;Link/Unlink Data Share Target&lt;/strong&gt;, select the V2 target, and save.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The target identifies the receiving account. The share identifies the data. Creating a target alone does not publish any objects. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/salesforce/setup-salesforce" rel="noopener noreferrer"&gt;Salesforce-side setup in Snowflake’s guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in Snowflake, check the connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="n"&gt;ZEROCOPY&lt;/span&gt; &lt;span class="n"&gt;CONNECTOR&lt;/span&gt;
  &lt;span class="n"&gt;PLATFORM_ADMIN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMINISTRATION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SALESFORCE_ZC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for &lt;code&gt;CONNECTED&lt;/code&gt; before mounting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Mount the shares with legacy names available
&lt;/h2&gt;

&lt;p&gt;I mounted all shares and enabled the legacy schema alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;SYSTEM&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ZEROCOPY_CONNECTOR_LIST_SHARES&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'PLATFORM_ADMIN.ADMINISTRATION.SALESFORCE_ZC'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;
  &lt;span class="n"&gt;LINKED_ZEROCOPY_CONNECTOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CONNECTOR_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PLATFORM_ADMIN.ADMINISTRATION.SALESFORCE_ZC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ALL_SHARES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LEGACY_SALESFORCE_SCHEMA_ALIAS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;SCHEMAS&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LEGACY_SALESFORCE_SCHEMA_ALIAS = TRUE&lt;/code&gt; keeps the &lt;code&gt;schema_&amp;lt;share_name&amp;gt;&lt;/code&gt; alias available, helping existing queries survive the migration. For a generic share called &lt;code&gt;CRM_DATA&lt;/code&gt;, this example uses &lt;code&gt;"schema_CRM_DATA"&lt;/code&gt;. Use the exact case returned in your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;VIEWS&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"schema_CRM_DATA"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use those views for queries. They can take about a minute to appear after mounting. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/salesforce/explore-data-products" rel="noopener noreferrer"&gt;Mounting and querying&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example reflects the &lt;code&gt;CREATE OR REPLACE&lt;/code&gt; form I used. For a new database, plain &lt;code&gt;CREATE DATABASE&lt;/code&gt; is sufficient; replacing an existing database is a separate operation, not a required connection step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Give the consuming role access
&lt;/h2&gt;

&lt;p&gt;Mounting the database does not finish the consumer setup. I granted database and schema access, followed by access to both existing and future views:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;
  &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_ANALYST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"schema_CRM_DATA"&lt;/span&gt;
  &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_ANALYST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;VIEWS&lt;/span&gt;
  &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"schema_CRM_DATA"&lt;/span&gt;
  &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_ANALYST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;FUTURE&lt;/span&gt; &lt;span class="n"&gt;VIEWS&lt;/span&gt;
  &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_SHARED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"schema_CRM_DATA"&lt;/span&gt;
  &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;SALESFORCE_ANALYST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ALL VIEWS&lt;/code&gt; covers existing views; &lt;code&gt;FUTURE VIEWS&lt;/code&gt; covers subsequently created views in that schema. Repeat for other permitted schemas. These grants do not cover future schemas or warehouse access. &lt;a href="https://docs.snowflake.com/en/sql-reference/sql/grant-privilege" rel="noopener noreferrer"&gt;Snowflake grant syntax&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaner names without breaking old references
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Legacy experience&lt;/th&gt;
&lt;th&gt;V2 naming option&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;schema_brz_data&lt;/code&gt; schema alias&lt;/td&gt;
&lt;td&gt;Optional; I enabled it in this setup to retain the legacy name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Case_Home__dll&lt;/code&gt; object&lt;/td&gt;
&lt;td&gt;An additional friendly view named &lt;code&gt;Case_Home&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;AccountId__c&lt;/code&gt; column&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AccountId&lt;/code&gt; in the friendly view&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are aliases, not a rename of Salesforce’s source objects. Friendly names default to enabled, so my SQL leaves &lt;code&gt;SALESFORCE_USER_FRIENDLY_NAME&lt;/code&gt; unset. Both naming options are creation-time choices. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/salesforce/explore-data-products" rel="noopener noreferrer"&gt;Naming options&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where my incremental test stopped
&lt;/h2&gt;

&lt;p&gt;Being able to query current data is different from knowing which records changed since the last run.&lt;/p&gt;

&lt;p&gt;My stream test on an exposed view failed with this error (object identifiers omitted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Change tracking is not supported on queries with
'Unmanaged Iceberg Table' inside views.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My understanding is that this exposed-view path did not support the change tracking needed for the stream.&lt;/strong&gt; Current-state queries worked; an insert/update/delete feed through that path did not.&lt;/p&gt;

&lt;p&gt;That is narrower than saying “V2 does not support incremental processing.” Salesforce’s release notes explicitly describe using streams and tasks with V2. Snowflake’s general documentation also distinguishes stream support by table type and Iceberg version; streams on views require change tracking on the underlying tables. An insert-only stream is not a substitute for a complete update/delete feed. &lt;a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_cdp_2026_summer_snowflake_v2.htm&amp;amp;language=en_US&amp;amp;type=5" rel="noopener noreferrer"&gt;Salesforce’s stated capability&lt;/a&gt;, &lt;a href="https://docs.snowflake.com/en/user-guide/streams-intro" rel="noopener noreferrer"&gt;Snowflake stream support and limitations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The connector guides do not resolve this specific error. Before relying on downstream history, I would confirm the supported source object and stream configuration, then test one insert, one update, and one delete end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving an existing connection
&lt;/h2&gt;

&lt;p&gt;We captured grants before the change, applied the required grants to the new setup, and captured them again to compare. We did not assume that mounting the database would preserve access automatically.&lt;/p&gt;

&lt;p&gt;That gives the migration two separate checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt; do the database, schema, and view names used by downstream queries still resolve?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access:&lt;/strong&gt; do the consuming roles retain the required privileges on the new objects?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep the legacy target linked while validating V2, and test representative queries under the consuming roles before unlinking it. Legacy schema aliases help preserve schema references, but do not replace the grants check. Snowflake documents the connection and database-switch procedure; existing connections are not upgraded automatically. &lt;a href="https://docs.snowflake.com/en/user-guide/data-integration/zero-copy/salesforce/migrate-legacy-share" rel="noopener noreferrer"&gt;Migration guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For existing users:&lt;/strong&gt; Salesforce’s current release notes specify October 31, 2026 for migrating legacy targets. Check that notice against your org’s rollout and migration plan. &lt;a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_cdp_2026_summer_snowflake_v2.htm&amp;amp;language=en_US&amp;amp;type=5" rel="noopener noreferrer"&gt;Migration notice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you used streams successfully with these Salesforce V2 objects? Which source object and stream configuration worked for you?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/LALITHASWAROOPK/salesforce-snowflake-zero-copy" rel="noopener noreferrer"&gt;GitHub examples&lt;/a&gt; include the V2 setup SQL, consumer grants, and a before-and-after migration checklist.&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>salesforce</category>
      <category>dataengineering</category>
      <category>zerocopy</category>
    </item>
    <item>
      <title>When Zero-Copy and Mirroring Clash: An Iceberg Metadata Gotcha — Part 3</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:22:51 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/when-zero-copy-and-mirroring-clash-an-iceberg-metadata-gotcha-part-3-de3</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/when-zero-copy-and-mirroring-clash-an-iceberg-metadata-gotcha-part-3-de3</guid>
      <description>&lt;p&gt;Zero-copy and mirroring were supposed to be teammates. Iceberg metadata became the sticking point.&lt;/p&gt;

&lt;p&gt;Snowflake's zero-copy clones let us create table copies without duplicating the existing data files. In our Fabric mirroring workflow, the Iceberg source also needed to convert into virtual Delta metadata for consumption.&lt;/p&gt;

&lt;p&gt;The source table worked in Snowflake. Fabric's conversion log said &lt;code&gt;BAD_METADATA&lt;/code&gt;. The data was present; the clash was in how the table's metadata was handled.&lt;/p&gt;

&lt;p&gt;I got all eight tables in this test converting again with a controlled insert, delete, and retry. The temporary row was removed. The business data ended where it started—but the conversion recovered.&lt;/p&gt;

&lt;p&gt;Here's what made this a new gotcha.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-copy still needs the right metadata
&lt;/h2&gt;

&lt;p&gt;Iceberg stores data in files and uses metadata to describe which files make up a table. Think of the metadata as its map.&lt;/p&gt;

&lt;p&gt;In our setup, Fabric accessed the Iceberg source through a shortcut and generated virtual Delta metadata for consumption. The source being readable in Snowflake didn't prove that this conversion had succeeded.&lt;/p&gt;

&lt;p&gt;The diagnostic reported competing metadata sets: a low-version metadata file had been modified more recently than a higher-version file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The newest file wasn't necessarily the latest version of the production table.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A clone brings another table identity
&lt;/h2&gt;

&lt;p&gt;We had cloned the source tables into other databases. In the cases inspected, source and clone metadata paths led into the same metadata directory.&lt;/p&gt;

&lt;p&gt;Cloning doesn't need to change the production rows to create new metadata. Snowflake gives an Iceberg clone distinct metadata, including its own &lt;code&gt;table-uuid&lt;/code&gt;. It shares underlying storage, but represents a separate table. &lt;a href="https://docs.snowflake.com/en/user-guide/object-clone" rel="noopener noreferrer"&gt;Snowflake documents this behavior&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That combination matters for quiet reference tables: production may not have had a recent write, while a newer clone has introduced metadata for another table identity.&lt;/p&gt;

&lt;p&gt;Our working explanation is that clone metadata interfered with Fabric's discovery of the intended source metadata. Working tables also had clones, so clone presence alone wasn't a diagnosis; the exact file-selection mechanism remains under investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recovery I tested
&lt;/h2&gt;

&lt;p&gt;For the affected source tables, I:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inserted a temporary row.&lt;/li&gt;
&lt;li&gt;Deleted that exact row.&lt;/li&gt;
&lt;li&gt;Retried the Fabric conversion.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Fabric conversion succeeded for all eight tested tables.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I checked the metadata after the insert and delete. Each of the eight tables advanced by two metadata versions, and the production metadata files had the newest S3 modification timestamps. The writes changed the metadata even though the temporary rows were removed.&lt;/p&gt;

&lt;p&gt;Microsoft's guidance also describes committing a source change to trigger another conversion attempt. &lt;a href="https://learn.microsoft.com/en-us/fabric/onelake/onelake-iceberg-tables#troubleshooting" rel="noopener noreferrer"&gt;OneLake troubleshooting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a targeted recovery, not a permanent fix. A temporary row needs a verified unused identifier, and downstream change consumers may still see the insert and delete. I wouldn't automate dummy writes across production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check on the next failure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Read &lt;code&gt;latest_conversion_log.txt&lt;/code&gt; under the shortcut's virtual &lt;code&gt;_delta_log&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Compare the source and known clone metadata paths, table identities, and actual storage modification times.&lt;/li&gt;
&lt;li&gt;After a retry, check conversion status and validate the intended table state through Fabric.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;LAST_ALTERED&lt;/code&gt; can help prioritize candidates, but it isn't an S3 file timestamp or proof that two tables are clones. It includes DDL, DML, and background metadata operations. &lt;a href="https://docs.snowflake.com/en/sql-reference/account-usage/tables#usage-notes" rel="noopener noreferrer"&gt;Snowflake's TABLES view notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Microsoft documents multiple metadata sets in one Iceberg folder as a conversion limitation. Its cleanup example concerns dropped and recreated tables; don't apply that cleanup blindly to files used by live clones.&lt;/p&gt;

&lt;p&gt;The lasting fix needs to make the intended table metadata unambiguous to the consumer. For now, the tested recovery gives us a way forward while that is investigated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-copy can share the files. Iceberg metadata still has to tell the reader which table those files belong to.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Previous gotchas: &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/what-i-learned-running-snowflake-iceberg-mirroring-in-microsoft-fabric-and-debugging-real-1151"&gt;Part 1&lt;/a&gt; · &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/snowflake-iceberg-fabric-what-we-tried-what-support-confirmed-and-what-comes-next-ie0"&gt;Part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have you seen a source table stay readable while its Fabric conversion failed?&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>snowflake</category>
      <category>microsoftfabric</category>
      <category>iceberg</category>
    </item>
    <item>
      <title>Same Shared Data. Different Answers. The Snowflake Timezone Trap</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Tue, 13 Oct 2026 19:04:20 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/same-shared-data-different-answers-the-snowflake-timezone-trap-20gd</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/same-shared-data-different-answers-the-snowflake-timezone-trap-20gd</guid>
      <description>&lt;p&gt;The provider could see the amount. The consuming application received NULL.&lt;/p&gt;

&lt;p&gt;Both were using the same shared data. The query ran successfully. The orders were there. Yet a calculated amount was missing, and a downstream application workflow was blocked.&lt;/p&gt;

&lt;p&gt;We started with the usual suspects: permissions, masking policies and changes to the share. The issue turned out to be inside a currency-rate join—where a calendar date was being compared with a timestamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data was present. The join was missing it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An anonymized case with simplified SQL and illustrative visuals.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the order appeared but the amount disappeared
&lt;/h2&gt;

&lt;p&gt;The application consumed a view built on shared data. Its amount field depended on finding an exchange rate for the order's currency and booking date.&lt;/p&gt;

&lt;p&gt;The relevant logic looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local_amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversion_rate&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;converted_amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;exchange_rates&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency_code&lt;/span&gt;
   &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;book_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversion_timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The currency condition looked ordinary. The date condition hid the problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;book_date&lt;/code&gt; was a &lt;code&gt;DATE&lt;/code&gt;: a calendar day.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;conversion_timestamp&lt;/code&gt; was a &lt;code&gt;TIMESTAMP_LTZ&lt;/code&gt;: a moment interpreted using the session timezone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The query was asking whether a day and an instant were equal. That required an implicit conversion, and the session's timezone became part of the comparison.&lt;/p&gt;

&lt;p&gt;Because it was a &lt;code&gt;LEFT JOIN&lt;/code&gt;, a missed match preserved the order and supplied NULL for the rate. Multiplying the amount by that missing rate produced NULL too.&lt;/p&gt;

&lt;p&gt;That is why a successful query and valid source data were not enough to rule out a calculation problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv3huvdb41y2kwztgw1n5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv3huvdb41y2kwztgw1n5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The timezone difference hiding behind the SQL
&lt;/h2&gt;

&lt;p&gt;The provider environment used UTC. The consumer was believed to be using Pacific time. That difference gave us a reason to examine the date comparison instead of treating the NULL as a sharing problem.&lt;/p&gt;

&lt;p&gt;Snowflake stores &lt;code&gt;TIMESTAMP_LTZ&lt;/code&gt; internally in UTC, while its operations use the session timezone. &lt;a href="https://docs.snowflake.com/en/sql-reference/data-types-datetime" rel="noopener noreferrer"&gt;Snowflake timestamp types&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider a rate timestamp at midnight UTC. A date interpreted as midnight in Los Angeles represents a different instant. During daylight saving, local midnight is seven hours later in UTC; in winter, it is eight hours later.&lt;/p&gt;

&lt;p&gt;The same source timestamp can therefore be involved in a matching comparison in one session and a failed comparison in another.&lt;/p&gt;

&lt;p&gt;No rows need to change for this to happen. A session setting is enough to change the interpretation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsixjkukwfz3clvsfhhmb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsixjkukwfz3clvsfhhmb.gif" alt=" " width="760" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible fixes and what each changes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Option 1: align the consumer's session timezone with the provider.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a UTC business-date rule, the session workaround is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;TIMEZONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the session aligned to UTC, the example's midnight comparison can match. This is a useful diagnostic and possible workaround, but every consuming connection must use the intended setting. Changing it can also affect other date and timestamp logic in the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2: extract the date before comparing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the intended comparison is at calendar-date grain, make the conversion explicit on the rate side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Before&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;book_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversion_timestamp&lt;/span&gt;

&lt;span class="c1"&gt;-- Explicit date comparison&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;book_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TO_DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversion_timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a comparison between two DATE expressions. It removes the time-of-day equality requirement, but the extracted date must still be the intended business date. With an LTZ input, &lt;code&gt;TO_DATE()&lt;/code&gt; can return different calendar dates under different session timezones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ideal fix for a UTC business-date rule
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Normalize to the business timezone first, then extract the date.&lt;/strong&gt; This makes the date rule explicit in the shared view instead of depending on each consumer's session setting.&lt;/p&gt;

&lt;p&gt;For LTZ rate timestamps whose business date is defined in UTC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;book_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TO_DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CONVERT_TIMEZONE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversion_timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The conversion produces a timestamp with an explicit UTC offset; &lt;code&gt;TO_DATE()&lt;/code&gt; then extracts that calendar date. For the same LTZ instant, the resulting UTC date stays consistent whether the consuming session uses UTC or Los Angeles. For a regional business calendar, substitute the intended region. NTZ inputs also need their source timezone defined. &lt;a href="https://docs.snowflake.com/en/sql-reference/functions/convert_timezone" rel="noopener noreferrer"&gt;Snowflake timezone conversion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why does that matter? Midnight UTC can be the previous calendar day in Pacific time. If a rate exists for the adjacent day, a shifted date might return an amount rather than NULL while selecting a different day's rate.&lt;/p&gt;

&lt;p&gt;So the validation question is more precise than “did the amount come back?” It is: &lt;strong&gt;“did the order match the intended business date's rate?”&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Expected behavior for the midnight-UTC example&lt;/th&gt;
&lt;th&gt;Remaining dependency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Set the session to UTC&lt;/td&gt;
&lt;td&gt;The date-to-timestamp comparison can match&lt;/td&gt;
&lt;td&gt;Every consuming connection needs the intended setting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;TO_DATE()&lt;/code&gt; alone&lt;/td&gt;
&lt;td&gt;Compare calendar dates instead of exact instants&lt;/td&gt;
&lt;td&gt;The extracted LTZ date can shift with the session timezone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convert to UTC, then use &lt;code&gt;TO_DATE()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Compare the explicitly defined UTC business date&lt;/td&gt;
&lt;td&gt;UTC must be the correct business calendar; the rate key must identify the intended row&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are expected behaviors of the simplified example, not measured production outcomes. Explicit timezone conversion cannot supply a genuinely missing rate or resolve multiple rates for the same key.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would test for the next shared view
&lt;/h2&gt;

&lt;p&gt;A small set of checks can expose this class of issue early:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect the effective timezone in the actual application connection with &lt;code&gt;SHOW PARAMETERS LIKE 'TIMEZONE' IN SESSION&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test the same date logic under the provider and consumer session settings, including winter and summer boundaries.&lt;/li&gt;
&lt;li&gt;Check the selected rate's business date, not just whether the calculated amount is populated.&lt;/li&gt;
&lt;li&gt;Confirm that currency plus the chosen date identifies the intended rate. Multiple matches can duplicate orders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Account defaults are useful context, but users and sessions can override them. The application's effective setting is what matters. &lt;a href="https://docs.snowflake.com/en/sql-reference/parameters" rel="noopener noreferrer"&gt;Snowflake parameter hierarchy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7lu1f9c7o4s4t2nl62q1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7lu1f9c7o4s4t2nl62q1.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The lesson I took from this was simple: when the source has a value and the consumer sees NULL, follow the calculation. An unmatched join can look like missing data, and an implicit conversion can quietly make the result depend on who runs the query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A shared view should make the business meaning of its dates explicit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you encountered a query that succeeded while an important join quietly failed? Share the pattern in the comments, or &lt;a href="https://www.linkedin.com/in/krishna-tangudu-1a961319/" rel="noopener noreferrer"&gt;connect with me on LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>sql</category>
      <category>database</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Leaving SAP HANA? Understand What You’re Taking With You.</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Sun, 04 Oct 2026 07:33:53 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/leaving-sap-hana-understand-what-youre-taking-with-you-5h70</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/leaving-sap-hana-understand-what-youre-taking-with-you-5h70</guid>
      <description>&lt;p&gt;&lt;em&gt;How we are using an enterprise data agent to understand HANA logic before deciding what to rebuild in Snowflake or Microsoft Fabric.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A HANA report can be rebuilt in Snowflake or Microsoft Fabric, run successfully, and still change what its columns mean.&lt;/p&gt;

&lt;p&gt;Take a reference number. Some records get it from a table, others through an older reporting model, and manually entered records get fixed text. Rebuilding all three the same way could change reporting behavior without producing a SQL error.&lt;/p&gt;

&lt;p&gt;Our enterprise data agent runs on Snowflake Cortex and helps investigate that logic. The useful outcome is a handoff an engineer can inspect before deciding what to preserve, simplify or retire: &lt;strong&gt;what is understood, what is missing, and what still needs testing.&lt;/strong&gt; The format below is our proposed handoff, not an automatically validated migration deliverable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F11ho7fmvbs4ziim5bt0u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F11ho7fmvbs4ziim5bt0u.png" alt="A fictional engineer handoff: understood source logic, an unresolved archive path, and a proposed test that has not been run" width="799" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Engineering cases are anonymized. The service-report example is fictional, not SAP-delivered content or production output. Results come from selected tests, not a controlled benchmark.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One report column, three different routes
&lt;/h2&gt;

&lt;p&gt;Think of a HANA calculation view as a recipe for a dataset: where records come from and how to combine, filter or calculate them. A view can read other views, creating &lt;strong&gt;layers&lt;/strong&gt;. It can also combine separate inputs, or &lt;strong&gt;branches&lt;/strong&gt;, each of which may pass through several layers.&lt;/p&gt;

&lt;p&gt;Think of a service report that combines current records, archived records and manually entered records. All three contribute to the same &lt;code&gt;ReferenceNumber&lt;/code&gt; column:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxr146y230vxi8issurvt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxr146y230vxi8issurvt.gif" alt="Moving dots trace the report’s input paths and stop at missing archive evidence" width="720" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The moving dots follow each input path, stopping where archive evidence is missing. An engineer needs to know what happens along those paths: which field supplies the value, how it changes, and whether records are filtered out. Our early answers could name the view without finishing that explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the logic, then follow its sources
&lt;/h2&gt;

&lt;p&gt;First, the agent needed the actual logic. In one case, its searchable material contained a model's outer definition but not the calculations underneath—like a recipe title without the instructions. We added deployed view definitions and executable code, preserving full names and locations to distinguish similar models.&lt;/p&gt;

&lt;p&gt;Then we changed how the material was split up.&lt;/p&gt;

&lt;p&gt;Large definitions had been divided into smaller pieces for search. This is often called &lt;strong&gt;chunking&lt;/strong&gt;. A search for &lt;code&gt;ReferenceNumber&lt;/code&gt; might return the current-record mapping while missing the archived-record mapping elsewhere in the definition.&lt;/p&gt;

&lt;p&gt;For our example, the current-record mapping says which field supplies the reference number. The archive mapping describes another source, and the manual mapping supplies fixed text. Returning just the first one could make an engineer miss two different behaviors.&lt;/p&gt;

&lt;p&gt;We changed the grouping so those related mappings could be retrieved together. Search helped find the model; an exact lookup then retrieved its evidence and checked whether every expected input was present.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyi0xd3626fkhs86qaoak.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyi0xd3626fkhs86qaoak.gif" alt="Animated comparison of one search hit with all mappings grouped for the same output column" width="760" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The lesson was practical: &lt;strong&gt;the way you divide source material affects what the agent can explain.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even the right source name may be an intermediate step. HANA models can use a &lt;strong&gt;synonym&lt;/strong&gt;, an alias that points to another object. We added those deployed alias connections and a field catalog describing mappings and relationships. If an alias led to another view, the investigation had to continue inside it.&lt;/p&gt;

&lt;p&gt;For this report, that means the current-record path can reach an activity table. The archived-record path must remain unresolved until the deeper definition is available. The manual-record path ends at fixed text; there is no source field behind that value.&lt;/p&gt;

&lt;p&gt;After these changes, a recorded test of a selected model and column returned physical source paths that the earlier answer had left unresolved. That was a useful improvement, with a clear limit: resolving those paths did not prove complete coverage of every HANA model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correct evidence still needed a faithful explanation
&lt;/h2&gt;

&lt;p&gt;Our previous article asked whether an agent's evaluation was judging the right thing. Here, the practical test was whether its explanation preserved the source behavior.&lt;/p&gt;

&lt;p&gt;We found answers that retrieved the right mappings but counted them incorrectly. A parser—a small program that reads the model definition and checks its structure—could count the inputs, but the agent did not consistently run it. We put the count checks into the existing lookup so the answer received checked facts.&lt;/p&gt;

&lt;p&gt;Another answer described a fixed placeholder as SQL &lt;code&gt;NULL&lt;/code&gt;. In the fictional report, that would mean treating &lt;code&gt;NOT_APPLICABLE&lt;/code&gt; as a missing value. &lt;code&gt;NULL&lt;/code&gt; means no value; &lt;code&gt;NOT_APPLICABLE&lt;/code&gt; is actual text. A filter or report can treat them differently. An engineer following the explanation could therefore change the behavior accidentally.&lt;/p&gt;

&lt;p&gt;We corrected the instructions and repeated an ordinary question. The selected test returned the expected paths and preserved the literal values. One generated query still failed before the agent recovered, so this was progress rather than error-free execution.&lt;/p&gt;

&lt;p&gt;We also made incomplete answers harder to overlook. The deployed response format uses an orange &lt;strong&gt;PARTIAL&lt;/strong&gt; notice for missing evidence and a red &lt;strong&gt;UNVERIFIED&lt;/strong&gt; notice for unsupported conclusions. In our fictional example, the useful message is specific: “The archive path stops at a view whose definition is unavailable.” A warning should name the gap, not just say confidence is low.&lt;/p&gt;

&lt;p&gt;Our own guidance also needed attention: one rule allowed guesses when local evidence was missing, while another required evidence. Parsing was optional in one place and required in another. The design now gives shared rules to the agent instructions and detailed HANA steps to a &lt;strong&gt;skill&lt;/strong&gt;, a task-specific playbook. We also deployed a cleanup that removed duplicated instructions while retaining each rule once. This is easier to maintain; better accuracy or speed still requires evaluation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn7h20wl8zujn7ecehwz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn7h20wl8zujn7ecehwz1.png" alt="Shared agent rules and the detailed HANA skill have different responsibilities; reliability requires testing" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For an engineer, the acceptance test remains concrete: account for every input path, preserve calculations and fixed values, and state where the evidence stops.&lt;/p&gt;

&lt;p&gt;To make recurring questions easier, we added reviewed question-and-SQL examples through &lt;a href="https://docs.snowflake.com/en/user-guide/views-semantic/verified-query-repository" rel="noopener noreferrer"&gt;Cortex Analyst's Verified Query Repository&lt;/a&gt;. A query that worked directly against the database initially failed through the agent's more limited tool. Splitting retrieval across the appropriate tools corrected that tested request. The lesson: test through the interface the agent actually uses; a verified example guides retrieval but does not certify the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the old design gives you choices
&lt;/h2&gt;

&lt;p&gt;Now return to the migration decision. Knowing how the report works does not mean rebuilding every view exactly as it was.&lt;/p&gt;

&lt;p&gt;An engineer might preserve a business rule, simplify repeated calculations, or retire an input that the business no longer needs. For this report:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evidence in the fictional example&lt;/th&gt;
&lt;th&gt;Engineering decision&lt;/th&gt;
&lt;th&gt;Next validation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current records resolve to a known field&lt;/td&gt;
&lt;td&gt;Preserve its meaning in the target mapping.&lt;/td&gt;
&lt;td&gt;Compare matched records from the same source snapshot, including any transformations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The archived path stops at another view&lt;/td&gt;
&lt;td&gt;Investigate before implementing, retaining or retiring that path.&lt;/td&gt;
&lt;td&gt;Obtain the missing definition and check its mappings and filters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual records contain &lt;code&gt;NOT_APPLICABLE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Keep the literal, or explicitly agree a different representation.&lt;/td&gt;
&lt;td&gt;Test the literal separately from SQL &lt;code&gt;NULL&lt;/code&gt;, including how reports filter each.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are proposed engineering checks, not tests completed for this fictional model.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A source explanation is not a data test.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reading a model definition can establish mappings and filters. It cannot establish the row counts produced by a rebuilt query. If the agent cannot execute that query through an available tool, label the handoff &lt;strong&gt;“Query prepared — not executed”&lt;/strong&gt; and leave the results unknown. Asking it to continue does not create access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Snowflake or Fabric, one possible design is an activity fact table, where each row represents a defined event, with shared customer and service dimensions holding descriptive information. In Fabric, a Power BI semantic model could then define the reporting measures and relationships. Some workloads may still benefit from views.&lt;/p&gt;

&lt;p&gt;This is an architectural choice, not a rule that HANA uses views and other platforms use facts and dimensions. HANA supports dimensional modeling too. &lt;a href="https://help.sap.com/docs/SAP_HANA_PLATFORM/57a523b496cc4531a3676f5d94644899/69ffef33b3a142dfa9f3c8b19f82ea6c.html" rel="noopener noreferrer"&gt;SAP HANA modeling documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conversion tools have a place in that journey. &lt;a href="https://docs.snowflake.com/en/migrations/snowconvert-docs/general/about" rel="noopener noreferrer"&gt;SnowConvert AI&lt;/a&gt; and Microsoft's &lt;a href="https://learn.microsoft.com/en-us/fabric/data-warehouse/migration-assistant" rel="noopener noreferrer"&gt;Fabric Migration Assistant&lt;/a&gt; support migration work for their documented source platforms. SAP HANA is not listed on those two capability pages checked on October 3, 2026. Our work explores how source understanding can inform the decisions around a migration; we have not demonstrated automatic migration or equivalent results on a target platform.&lt;/p&gt;

&lt;p&gt;For teams building migration tools, that raises an interesting question: &lt;strong&gt;could the workflow show engineers what they are about to rebuild—and the evidence behind it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This continues our series from &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/moving-data-is-easier-than-moving-knowledge-2co4"&gt;recovering legacy knowledge&lt;/a&gt;, through &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/natural-language-is-the-interface-not-the-semantic-layer-ce9"&gt;governed semantics&lt;/a&gt; and &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/my-snowflake-agent-was-wrong-so-was-my-evaluation-1b46"&gt;evaluating agent answers&lt;/a&gt;, into a specific engineering task.&lt;/p&gt;

&lt;p&gt;Before asking how to move a HANA model, we can ask a better question: &lt;strong&gt;what does it do, and which parts still belong in the new design?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to explore the approach or ask about the code? &lt;a href="https://www.linkedin.com/in/krishna-tangudu-1a961319/" rel="noopener noreferrer"&gt;Connect with me on LinkedIn&lt;/a&gt;. Tell me which part interests you: collecting HANA definitions, finding the relevant logic, or checking where fields come from.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>sap</category>
      <category>microsoftfabric</category>
      <category>mcp</category>
    </item>
    <item>
      <title>My Snowflake Agent Was Wrong. So Was My Evaluation.</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Mon, 28 Sep 2026 03:00:59 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/my-snowflake-agent-was-wrong-so-was-my-evaluation-1b46</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/my-snowflake-agent-was-wrong-so-was-my-evaluation-1b46</guid>
      <description>&lt;p&gt;&lt;em&gt;What production conversations taught me about fixing the right layer—and checking whether the fix actually worked.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When an agent gets a disappointing evaluation score, I now ask three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did it answer the question correctly?&lt;/li&gt;
&lt;li&gt;Did it take an appropriate path to the answer?&lt;/li&gt;
&lt;li&gt;Was the evaluation measuring the behavior I actually wanted?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In my Snowflake agent, those answers did not always agree.&lt;/strong&gt; A revised lookup recovered an object but still received partial tool-selection credit. Other test expectations omitted steps my instructions required. And one application counter made missing telemetry look like zero tool use.&lt;/p&gt;

&lt;p&gt;I was maintaining three things at once: the agent, the evidence about its behavior, and the tests judging it. Changing the prompt was only one possible fix.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;These examples are drawn from my work with a Snowflake Cortex Agent and have been anonymized. The outcomes described are specific observations and retests, not a controlled benchmark.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix lived between the agent and its tool
&lt;/h2&gt;

&lt;p&gt;My agent said an object did not exist. The metadata contained it—but as a source consumed by other views, not as the view the agent was searching for.&lt;/p&gt;

&lt;p&gt;I added a fallback instruction. The lookup still failed.&lt;/p&gt;

&lt;p&gt;The investigation then suggested the semantic tool could not search by source. Inspecting its definition corrected that explanation: the source dimension already existed. Its SQL-generation guidance emphasized view-name searches.&lt;/p&gt;

&lt;p&gt;The successful revision changed both layers. The agent received a fallback telling it when to ask &lt;em&gt;which views consume this source?&lt;/em&gt; The semantic view received guidance explaining that lookup to Cortex Analyst. The recorded retest recovered the object and its consumers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc2j03dr0jgm00uzv9y61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc2j03dr0jgm00uzv9y61.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An anonymized reconstruction. The source dimension already existed; the change clarified its use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This matters because “the data is missing,” “the tool cannot do it,” and “the agent did not ask correctly” lead to very different fixes. The investigation briefly entertained each explanation. Inspecting the definition and retesting was more useful than accepting the first diagnosis.&lt;/p&gt;

&lt;p&gt;Even the successful retest had a boundary. Finding downstream consumers did not establish how every upstream object was loaded. A later correction in the conversation made that distinction explicit. A lineage lookup should not turn into an unsupported explanation of the ingestion architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The evidence I actually used
&lt;/h2&gt;

&lt;p&gt;I read real questions, responses, and follow-ups alongside native execution traces. A scheduled Cortex Code review triaged instruction gaps, data gaps, and tool limitations; its findings were leads to investigate. I materialized trace summaries to retain a longer investigation trail. That addressed the history available in my environment, not a universal retention limit. &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-monitor" rel="noopener noreferrer"&gt;Snowflake monitoring documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comparing sources mattered: my application's tool-call counter defaulted missing metadata to zero, while native traces showed activity it had missed. &lt;strong&gt;A missing measurement had been made to look like a measured zero.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I judge an evaluation
&lt;/h2&gt;

&lt;p&gt;“What was the score?” needs a second question: &lt;em&gt;the score for what?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Snowflake separates several checks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;What it judges&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Answer correctness&lt;/td&gt;
&lt;td&gt;An LLM judges the answer against expected content.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool selection accuracy&lt;/td&gt;
&lt;td&gt;Deterministic matching of expected and actual tool names and call counts; order is ignored.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool execution accuracy&lt;/td&gt;
&lt;td&gt;Expected inputs and outputs are compared with matching tool invocations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logical consistency&lt;/td&gt;
&lt;td&gt;An LLM checks consistency across instructions, planning, and actions without reference answers.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tool selection can penalize extra calls even when the answer improves. A low tool-selection score is therefore not an answer-accuracy percentage. The mechanics are in the appendix and &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-evaluations" rel="noopener noreferrer"&gt;Snowflake’s evaluation documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That distinction explained some of my confusing results. Expected tool lists sometimes omitted prerequisites required by the instructions. Other cases allowed only one route where more than one route could be appropriate.&lt;/p&gt;

&lt;p&gt;But I should not make a test easier just because the agent failed it. Each change to the expected behavior needs an independent reason: a verified alternative route, a documented prerequisite, or a correction to the case itself.&lt;/p&gt;

&lt;p&gt;The missing-object case illustrates why I inspect individual records. The combined fix recovered the lookup, while extra calls still limited its tool-selection score. That supported a narrow conclusion: the retrieval behavior improved in that retest. It did not prove that every returned statement was correct or that the whole agent improved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real questions are good test inputs. Old answers are not automatically ground truth.
&lt;/h3&gt;

&lt;p&gt;Real conversations supply questions I would not invent in a demo. Turning them into tests also creates traps. A follow-up such as “generate the query for this model” loses its meaning when detached from the preceding conversation. A previously successful API response may contain a wrong answer. A time-sensitive answer can become stale.&lt;/p&gt;

&lt;p&gt;For a useful regression case, I need the question's context, independently checked expectations, acceptable uncertainty, and the behavior that must not recur. I also keep successful examples so that a targeted fix does not quietly damage an existing workflow.&lt;/p&gt;

&lt;p&gt;Changing the questions or expected answers creates a new test baseline. Comparing its average with an older baseline as if only the agent changed would overstate the improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did the test exercise the capability?
&lt;/h3&gt;

&lt;p&gt;The traces also challenged what I thought my tests covered. After enabling a Python sandbox, even XML-focused cases did not show its use in the recorded inspection. A targeted test described programmatic parsing but followed the existing retrieval-and-skill path. &lt;strong&gt;Configured, mentioned in planning, and invoked are different states.&lt;/strong&gt; A correct answer through another route could pass an answer test while leaving the sandbox untested. I needed evidence of invocation and correct extraction before attributing an improvement to Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  The right answer can still be the wrong interaction
&lt;/h2&gt;

&lt;p&gt;Another incident involved an object name with words in the wrong order. The agent found a plausible alternative and began analysing it. The user had to correct the selection.&lt;/p&gt;

&lt;p&gt;I introduced similar-name search and confirmation. Tool checks showed that candidate retrieval worked. Then I tested through the application.&lt;/p&gt;

&lt;p&gt;The revised agent found the intended candidate—and continued into analysis without asking me to confirm it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval had improved. The interaction still violated the requirement.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs222hnyph3jxfhbeu1rn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs222hnyph3jxfhbeu1rn.gif" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Illustrative animation. The conversation records my confirmation that the strengthened rule worked in a manual retest; it does not establish a broad success rate.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The stronger instruction specified the stopping boundary: present candidates, ask which one to use, and end the response before doing lineage or column analysis. It also included examples of the unwanted and intended behavior.&lt;/p&gt;

&lt;p&gt;For this case, my proposed regression checks are concrete:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exact object exists&lt;/td&gt;
&lt;td&gt;Analyse that object.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact object is absent; alternatives exist&lt;/td&gt;
&lt;td&gt;Present candidates and ask; do not analyse a substitute yet.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No candidates exist&lt;/td&gt;
&lt;td&gt;Explain the search boundary without inventing an object.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User confirms a candidate&lt;/td&gt;
&lt;td&gt;Continue with the confirmed object.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here is a synthetic test pattern a reader can adapt. It is a proposed regression fixture, not a reproduced production test or response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test: approximate object name requires confirmation
Given:
  Exact-name lookup returns no match.
  Candidate lookup returns synthetic objects A and B.
Before: the agent chooses a candidate and begins analysis.
Required after:
  Present A and B with distinguishing context.
  Ask the user to choose, then end the turn.
Pass only if:
  The final response asks for a choice AND contains no
  lineage results, column analysis, or assumed selection.
Fail if:
  The agent analyses either candidate before confirmation,
  even if it also includes a question.
Next turn:
  User selects B; analysis must refer to B, not A.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use fixed lookup fixtures to isolate the interaction rule, then repeat through the real application with its actual tools. A question mark alone is not a pass. Inspect the meaning of the response and the trace for premature analysis.&lt;/p&gt;

&lt;p&gt;A final-answer similarity score alone would miss part of that contract. I need to check the turn where the agent was supposed to stop.&lt;/p&gt;

&lt;p&gt;There was a deeper reason to care about that pause. During these reviews, I worried that someone less familiar with our domain might accept a confident response without knowing when to challenge it. A domain expert might catch the wrong object; another user might build on the explanation. That concern was not a measured comparison between user groups. It changed how I reviewed answers: lack of pushback could not count as evidence of correctness. Asking for confirmation exposes a choice the agent would otherwise make silently, though confirmation alone does not verify the analysis that follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Each revision should explain its reason
&lt;/h2&gt;

&lt;p&gt;My changes were not all additions to the main prompt. They addressed different layers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Layer to investigate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wrong object selected without confirmation&lt;/td&gt;
&lt;td&gt;Agent interaction instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata exists but the lookup asks the wrong question&lt;/td&gt;
&lt;td&gt;Agent routing and semantic-view guidance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Specialist parsing or traversal is incomplete&lt;/td&gt;
&lt;td&gt;Domain skill and source coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needed computation is unavailable&lt;/td&gt;
&lt;td&gt;Tool capability, followed by invocation tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generated output cannot be accessed in the application&lt;/td&gt;
&lt;td&gt;Delivery channel and response format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plausible answer receives unexpected evaluation penalties&lt;/td&gt;
&lt;td&gt;Expected behavior and per-case scoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool use appears absent in one dashboard&lt;/td&gt;
&lt;td&gt;Instrumentation and native trace evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A deployment mistake changed my rule.&lt;/strong&gt; The agent was recreated during this work, resetting its native version history while our conversation still used the old labels. I explicitly said not to recreate it again without asking me. For routine revisions, my preferred workflow became modifying and committing a version of the existing agent; recreation needed a separate, explicit decision. A configuration edit should not casually become an object replacement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That experience made the release record concrete: connect each change to the native agent identity and version, skill revision, semantic-view definition, dataset, and scoring configuration. A conversation label cannot substitute for that record. The appendix shows selected version reasons.&lt;/p&gt;

&lt;p&gt;None of this happened in a quiet, finished post-mortem. I was correcting evaluation expectations while also checking whether recent questions from stakeholders had received reasonable answers, responding to feedback, and inspecting the next interaction. The service remained in use while I was learning how to evaluate it. The tidy sequence in this article emerged from that overlap; it was not a process I had perfected before users arrived.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would do first on the next agent
&lt;/h2&gt;

&lt;p&gt;I did not begin with a formal AI lifecycle. I began with people correcting the agent.&lt;/p&gt;

&lt;p&gt;That grew into a repeatable engineering practice: retain the relevant evidence, investigate the failure, change the appropriate layer, and test the behavior again. Keep observations separate from hypotheses. Keep a successful component test separate from an application retest. Keep a better evaluation score separate from a better answer.&lt;/p&gt;

&lt;p&gt;If I were starting again, I would write the regression case before the fix:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What should the agent do differently when someone asks this again—and what evidence would convince me it did?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question has been more useful than asking whether the agent is finally “good.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: the details behind the story
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Evaluation mechanics
&lt;/h3&gt;

&lt;p&gt;For tool selection, the documented formula is matched calls divided by the larger of expected entries or actual calls. An &lt;strong&gt;illustrative&lt;/strong&gt;, invented example: one expected call and four actual calls, with one match, scores 0.25. That does not mean the answer is 25% correct. Tool execution handles extra calls differently. Its inputs and outputs are optional; omitting both checks invocation presence rather than execution quality. Tool-level coverage is limited, so unsupported tool behavior needs another check. &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-evaluations" rel="noopener noreferrer"&gt;Cortex Agent evaluations&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Retaining an investigation trail
&lt;/h3&gt;

&lt;p&gt;I materialized trace summaries on a schedule to extend the investigation window, with known limitations around join precision and late-arriving spans.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwlncyque0k6kq57b4b1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwlncyque0k6kq57b4b1.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Selected version reasons
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcfjumrfkfe4nu78lvr85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcfjumrfkfe4nu78lvr85.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Selected native versions and their reasons. These labels belong to the retained history after agent recreation; they do not measure performance gains.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>ai</category>
      <category>testing</category>
      <category>agents</category>
    </item>
    <item>
      <title>Natural Language Is the Interface, Not the Semantic Layer</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Tue, 15 Sep 2026 18:48:24 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/natural-language-is-the-interface-not-the-semantic-layer-ce9</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/natural-language-is-the-interface-not-the-semantic-layer-ce9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is Part 2 of a two-part technical series. &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/moving-data-is-easier-than-moving-knowledge-2co4"&gt;Part 1&lt;/a&gt; showed how an Enterprise Data Discovery Assistant recovers legacy logic and produces grounded SQL. &lt;a href="https://medium.com/@ghrupakkumar/how-to-use-ai-agents-to-build-ai-ready-data-products-c19ea4895102" rel="noopener noreferrer"&gt;Rupak's article on AI-ready data products&lt;/a&gt; provides the larger context: agents need machine-readable semantics, active contracts, and engineering guardrails. This post follows the next step—from a business question to governed execution and an explained answer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;All names, identifiers, queries, and results below are synthetic. Quantities are rounded to communicate scale without publishing an internal inventory.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Natural language does not remove the need for data modeling. It makes good data modeling visible to more people.&lt;/p&gt;

&lt;p&gt;Our preview tests asked whether business users could explore an enterprise-scale Customer 360 domain—more than 100 million records across multiple curated tables—without first learning schemas, joins, or SQL.&lt;/p&gt;

&lt;p&gt;The answer was encouraging, with an important qualification: &lt;strong&gt;natural language is the interface, not the semantic layer&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question hides several decisions
&lt;/h2&gt;

&lt;p&gt;Consider a straightforward request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which customer segments had the highest order value last quarter, and how did that change from the previous quarter?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before SQL can run, the system must resolve what &lt;em&gt;order value&lt;/em&gt; means, which field represents &lt;em&gt;customer segment&lt;/em&gt;, how orders relate to customers, which calendar defines &lt;em&gt;last quarter&lt;/em&gt;, whether the user is allowed to see the requested data—and whether the data product is trustworthy right now.&lt;/p&gt;

&lt;p&gt;A language model looking only at table and column names may guess. A semantic view makes the analytical decisions explicit: grain, metrics, time semantics, filters, and supported relationships. Snowflake policies determine who may access the result, while our runtime contract check determines whether the data product is fit to use now.&lt;/p&gt;

&lt;p&gt;It defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logical tables, row grain, primary keys, and unique keys&lt;/strong&gt;, so the meaning of one row is explicit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimensions and time dimensions&lt;/strong&gt;, such as customer segment and fiscal quarter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facts and governed metrics&lt;/strong&gt;, including aggregation and distinct-count behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relationships&lt;/strong&gt;, including bridge tables for many-to-many associations, so join paths are modeled rather than improvised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filters, synonyms, and question-handling instructions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified queries&lt;/strong&gt;, which pair important question patterns with reviewed SQL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.snowflake.com/en/user-guide/views-semantic/semantic-view-yaml-spec" rel="noopener noreferrer"&gt;Snowflake recommends semantic views for new Cortex Analyst implementations&lt;/a&gt;. They are schema-level objects integrated with Snowflake privileges and metadata—not prompt text pretending to be governance.&lt;/p&gt;

&lt;p&gt;A semantic view tells the assistant &lt;strong&gt;what the data means&lt;/strong&gt;. A data contract tells it &lt;strong&gt;whether that data product is fit to use now&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For governed business questions in our preview, the assistant performs a mandatory contract check before using the analytical semantic view. It evaluates the target domain's contract health, freshness, latest validation result, and any failure reason.&lt;/p&gt;

&lt;p&gt;The contract check is application logic around the agent; it is not performed automatically by the semantic view itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  From question to governed answer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvksh3cbvn9exh0ypjacy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvksh3cbvn9exh0ypjacy.png" alt=" " width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flow is deliberately simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;Enterprise Data Assistant&lt;/strong&gt; identifies the user's intent and target business domain.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;data-contract check&lt;/strong&gt; evaluates contract status, freshness, quality, and the latest validation outcome before the business query runs.&lt;/li&gt;
&lt;li&gt;The contract result determines the response path: proceed normally when trustworthy, or attach the appropriate warning when the data is degraded, stale, inactive, or failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cortex Analyst&lt;/strong&gt; interprets the question using the selected semantic view, which supplies the governed dimensions, metrics, relationships, filters, and examples needed to generate SQL.&lt;/li&gt;
&lt;li&gt;The SQL runs inside the governed data platform. Snowflake privileges and data-protection policies still apply.&lt;/li&gt;
&lt;li&gt;The response returns the result together with the SQL, definitions, scope, and any required warning.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The distinction matters. When the contract is active, fresh, and passing, the check stays behind the scenes and the user gets a clean answer. A stale or degraded contract adds a caution. An inactive or failed contract produces a prominent warning that the result is not guaranteed before the preview attempts the query. For a higher-risk domain, the same decision point can be implemented as a hard stop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents" rel="noopener noreferrer"&gt;Cortex Agents can use Cortex Analyst for structured data and route across multiple semantic views&lt;/a&gt;. Cortex Analyst first attempts semantic SQL, where metrics, dimensions, and relationships come from the semantic view. When the modeled coverage cannot satisfy a request, Routing Mode can fall back to standard SQL on physical tables. That flexibility is useful, but for governed metrics the fallback should be treated as a different confidence path: expose it for review, request clarification, or block it for higher-risk questions rather than imply that it carries the same semantic guarantees. &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-analyst/cortex-analyst-routing-mode" rel="noopener noreferrer"&gt;Snowflake documents this behavior as Routing Mode&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The answer should show its work
&lt;/h2&gt;

&lt;p&gt;The experience should still feel conversational. The difference is that the answer exposes enough evidence for a user or engineer to challenge it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7sum0fj20momh56ew3n.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7sum0fj20momh56ew3n.gif" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Synthetic demonstration: no production interface or customer data is shown.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the example above, the response contract is more important than the visual polish. It should make six things clear:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What the user sees&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A concise answer&lt;/td&gt;
&lt;td&gt;The user gets the result without reading SQL first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metric and dimension definitions&lt;/td&gt;
&lt;td&gt;Business terms are not left open to interpretation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time range and filters&lt;/td&gt;
&lt;td&gt;The scope of the answer is explicit.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contract warning, when relevant&lt;/td&gt;
&lt;td&gt;Users know when freshness or quality may affect trust.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generated SQL&lt;/td&gt;
&lt;td&gt;An engineer can inspect and reproduce the query.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warnings or clarification&lt;/td&gt;
&lt;td&gt;Ambiguity is surfaced instead of silently resolved.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is a more useful standard than “the chatbot returned an answer.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Accuracy is an engineering loop
&lt;/h2&gt;

&lt;p&gt;Semantic metadata improves grounding, but it does not prove that every generated query is correct.&lt;/p&gt;

&lt;p&gt;Important question patterns should be captured in a &lt;strong&gt;Verified Query Repository&lt;/strong&gt;: a natural-language question paired with SQL whose logic and result have been validated by a qualified reviewer. The SQL should use the logical tables and columns defined by the semantic view rather than bypassing them for physical objects. Cortex Analyst can use relevant verified queries to guide similar requests. A verified example is guidance—not a blanket certification of every future answer. &lt;a href="https://docs.snowflake.com/en/user-guide/views-semantic/verified-query-repository" rel="noopener noreferrer"&gt;Snowflake's Verified Query Repository documentation&lt;/a&gt; makes that distinction concrete.&lt;/p&gt;

&lt;p&gt;The next layer is repeatable evaluation. Current Cortex Analyst evaluations compare generated SQL results with selected verified queries, track regressions, and record latency. This turns semantic-layer tuning into a measurable build-test-run-improve cycle instead of a collection of impressive demos. &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-analyst-evaluations" rel="noopener noreferrer"&gt;Snowflake documents the evaluation workflow here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also learned to compare results, not only SQL. Reconciliation must use the same as-of timestamp, fiscal calendar, currency logic, exclusion rules, and entity scope. Two syntactically valid queries can still answer different business questions.&lt;/p&gt;

&lt;p&gt;Our practical checklist is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with a narrow, coherent business domain.&lt;/li&gt;
&lt;li&gt;Define grain, keys, relationship cardinality, metrics, filters, and synonyms explicitly.&lt;/li&gt;
&lt;li&gt;Add reviewed queries for high-value and high-risk questions.&lt;/li&gt;
&lt;li&gt;Test ambiguous wording, invalid requests, boundary cases, and every contract state—not only the happy path.&lt;/li&gt;
&lt;li&gt;Track accuracy, regression, latency, and workload cost with representative questions.&lt;/li&gt;
&lt;li&gt;Show the generated SQL and scope when the audience needs traceability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Governance does not disappear behind chat
&lt;/h2&gt;

&lt;p&gt;A conversational interface must not become a shortcut around access controls.&lt;/p&gt;

&lt;p&gt;Access control and data contracts answer different questions. Privileges determine &lt;strong&gt;whether this user may access the data&lt;/strong&gt;. The contract check determines &lt;strong&gt;whether the selected data product should be trusted in its current state&lt;/strong&gt;. A governed assistant needs both decisions; neither replaces the other.&lt;/p&gt;

&lt;p&gt;Semantic views participate in Snowflake's privilege model. Row-access and masking policies applied to underlying tables can propagate to the semantic view and remain enforced. One subtle but important caution from Snowflake's guidance is that sample values stored as semantic metadata are not masked, so sensitive examples should not be embedded there. &lt;a href="https://docs.snowflake.com/en/user-guide/views-semantic/best-practices-dev" rel="noopener noreferrer"&gt;See Snowflake's development and deployment guidance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are product boundaries too. Cortex Analyst is designed for questions that can be resolved with SQL; it is not automatically a general business-insight engine. It also cannot refer to the results of a previous SQL query as if it had retained that result set. Those limits should shape both the user experience and the test suite. &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-analyst" rel="noopener noreferrer"&gt;The current limitations are documented here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed between Part 1 and Part 2
&lt;/h2&gt;

&lt;p&gt;Part 1 recovered the knowledge needed to rebuild a data product: joins, filters, calculations, ownership, and dependencies.&lt;/p&gt;

&lt;p&gt;Part 2 makes approved knowledge queryable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recovered legacy knowledge
          ↓
Governed data product + semantic view + contract
          ↓
Natural-language question
          ↓
Data-contract decision
          ↓
Semantic grounding + governed SQL
          ↓
Explained result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The language model is useful at the top of this stack because the hard decisions are represented beneath it.&lt;/p&gt;

&lt;p&gt;That is the larger lesson from the preview. Natural-language analytics is not a replacement for engineering discipline. Done well, it is a new interface to that discipline—one that lets more people ask useful questions while keeping definitions, access, SQL, and validation visible.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>snowflake</category>
      <category>dataengineering</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Mirroring Snowflake Iceberg into Microsoft Fabric : The Gotchas - Part 2:</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Thu, 10 Sep 2026 17:32:05 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/snowflake-iceberg-fabric-what-we-tried-what-support-confirmed-and-what-comes-next-ie0</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/snowflake-iceberg-fabric-what-we-tried-what-support-confirmed-and-what-comes-next-ie0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Previously:&lt;/strong&gt; In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/what-i-learned-running-snowflake-iceberg-mirroring-in-microsoft-fabric-and-debugging-real-1151"&gt;Mirroring Snowflake Iceberg into Microsoft Fabric: The Gotchas&lt;/a&gt;, I documented connection issues, permissions, and the difference between healthy mirroring and a usable SQL analytics endpoint.&lt;/p&gt;

&lt;p&gt;The goal remains the same: give analysts a shared serving layer in Fabric without making every downstream workflow query Snowflake independently.&lt;/p&gt;

&lt;p&gt;Since that post, two support investigations have added to the picture. One concerns the cost of continuous mirroring when the source changes only a few times daily. The other reverses an earlier troubleshooting pattern: the SQL analytics endpoint reads the data, but Spark fails.&lt;/p&gt;

&lt;p&gt;This is the checklist of what we have learned, which workarounds we have used, and the options we plan to evaluate next. &lt;strong&gt;Checked items describe experience or completed investigation. Unchecked items are future tests, not claimed results.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What we have done so far
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[x] &lt;strong&gt;Established the Snowflake–Fabric integration.&lt;/strong&gt; The first post covers the connection and permission fixes, including separate access to Iceberg storage.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Separated replication health from query behavior.&lt;/strong&gt; We validate actual results through the consuming engine, rather than treating a healthy status as sufficient.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Compared SQL analytics endpoint and Spark behavior on the decimal issue.&lt;/strong&gt; SQL succeeded where the affected Spark read failed.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Validated the source, table, and file schemas.&lt;/strong&gt; We submitted those findings to Microsoft support.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Tested the non-vectorized Spark workaround.&lt;/strong&gt; Reads succeeded; we subsequently reported performance and capacity impact.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Investigated mirroring schedules, restart behavior, and Delta CDF.&lt;/strong&gt; Support confirmed the limitations described below.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Measure whether Snowflake AWS-to-Azure replication improves the total economics.&lt;/strong&gt; This is our next architecture experiment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Continuous mirroring: the business clock matters
&lt;/h2&gt;

&lt;p&gt;Our source data changes approximately three times per day. The mirroring support case described a Snowflake cloud Layer cost spike and asked a practical question: can we run replication around those updates instead of continuously?&lt;/p&gt;

&lt;p&gt;At roughly 2,500 tables across the estate, maintaining an individual pipeline for every table would also be a substantial burden. That concern is about operational scale; it does not mean every batch design requires a separate pipeline per table.&lt;/p&gt;

&lt;p&gt;What the investigation established:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] &lt;strong&gt;Scheduling:&lt;/strong&gt; Microsoft documents no configurable mirroring schedules or replication windows today. &lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/snowflake#cost-optimization-recommendations" rel="noopener noreferrer"&gt;Snowflake mirroring guidance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Stop/start behavior:&lt;/strong&gt; support confirmed a full reload rather than continuation from the previous CDC position, consistent with the public FAQ. A timer around stop/start is therefore not equivalent to a scheduled incremental load. &lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-mirroring-faq" rel="noopener noreferrer"&gt;Snowflake mirroring FAQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Disabling the service user:&lt;/strong&gt; support explained that this is not a supported scheduling mechanism; connection retries and recovery become part of the problem.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Alternative suggested:&lt;/strong&gt; support recommended evaluating a Copy activity or Copy job, while acknowledging that this requires architectural work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The case is closed, but the correspondence acknowledges a scheduling limitation. It does not record delivery of a scheduling fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The decimal issue: SQL worked, Spark did not
&lt;/h2&gt;

&lt;p&gt;The second case captured this error, with the column anonymized:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parquet column cannot be converted ...
Column: [AMOUNT], Expected: decimal(15,2), Found: INT32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our diagnostics showed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Reported representation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake source column&lt;/td&gt;
&lt;td&gt;NUMBER(15,2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fabric table schema&lt;/td&gt;
&lt;td&gt;decimal(15,2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Examined Parquet file&lt;/td&gt;
&lt;td&gt;decimal(9,2), backed by INT32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL analytics endpoint&lt;/td&gt;
&lt;td&gt;Query succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Affected Spark read&lt;/td&gt;
&lt;td&gt;Failed with vectorization enabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spark with vectorization disabled&lt;/td&gt;
&lt;td&gt;Read succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The logical table schema and a file's physical encoding are different layers. The failing read path did not successfully reconcile the narrower file representation with the declared decimal type. The observed result was a read failure, not evidence of corrupted business values.&lt;/p&gt;

&lt;p&gt;Microsoft's public guidance lists the type-width issue and the Spark workaround. &lt;a href="https://learn.microsoft.com/en-us/fabric/onelake/onelake-iceberg-tables#limitations-and-considerations" rel="noopener noreferrer"&gt;OneLake Iceberg limitations&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] &lt;strong&gt;Workaround used:&lt;/strong&gt; disable the vectorized Parquet reader in the affected Spark session.&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Impact reported:&lt;/strong&gt; performance and capacity concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a scoped illustration of the workaround, using fictional table and column names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fabric PySpark: use a small, known slice of an affected table.
&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spark.sql.parquet.enableVectorizedReader&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        SELECT order_id, amount
        FROM dbo.orders_iceberg
        WHERE order_id IN (101, 102, 103)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action runs before the setting is restored. This example illustrates the configuration used; it is not a new benchmark or a reproduction executed for this article. A simple query showed a performance difference from &lt;em&gt;20 seconds to 2 minutes 26 seconds&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The lesson extends my previous post: SQL and Spark both need independent acceptance tests. Either engine can expose a limitation that the other does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Options on our checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Our status&lt;/th&gt;
&lt;th&gt;Why it is relevant&lt;/th&gt;
&lt;th&gt;What still needs proof&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Existing Snowflake mirroring / Iceberg integration&lt;/td&gt;
&lt;td&gt;Used; support cases investigated&lt;/td&gt;
&lt;td&gt;Shared Fabric serving layer&lt;/td&gt;
&lt;td&gt;Sustainable cost and consistent engine behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL analytics endpoint for affected reads&lt;/td&gt;
&lt;td&gt;Worked in the reported case&lt;/td&gt;
&lt;td&gt;Keeps compatible SQL workloads usable&lt;/td&gt;
&lt;td&gt;Coverage of workloads that currently require Spark&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spark non-vectorized reader&lt;/td&gt;
&lt;td&gt;Tested workaround&lt;/td&gt;
&lt;td&gt;Restores affected reads&lt;/td&gt;
&lt;td&gt;Performance and capacity at production scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled Copy job or reusable Copy pipeline&lt;/td&gt;
&lt;td&gt;Official option; not benchmarked here&lt;/td&gt;
&lt;td&gt;Direct control over movement frequency&lt;/td&gt;
&lt;td&gt;CDC eligibility, deletes, schema changes, cost, and management at scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake AWS → Snowflake Azure replication&lt;/td&gt;
&lt;td&gt;Planned next experiment&lt;/td&gt;
&lt;td&gt;Place a controlled replica nearer Azure consumers&lt;/td&gt;
&lt;td&gt;Eligible objects, consumer access, freshness, and total cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direct Snowflake access&lt;/td&gt;
&lt;td&gt;Tested workaround&lt;/td&gt;
&lt;td&gt;Keeps users productive&lt;/td&gt;
&lt;td&gt;Source load and total BI/query cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  5. What comes next: Snowflake AWS → Snowflake Azure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Our hypothesis:&lt;/strong&gt; a scheduled Azure replica may be cheaper to operate when it replaces substantial repeated reads across the cloud boundary. We have not established savings yet.&lt;/p&gt;

&lt;p&gt;The candidate flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Snowflake AWS source
        |
        | scheduled Snowflake replication
        v
Snowflake Azure secondary + target-region storage where required
        |
        | consumer path to validate separately
        v
Fabric Iceberg access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Snowflake officially supports replication across AWS and Azure within an organization. Replication groups provide read-only secondaries and configurable refresh schedules. Database replication is available across editions; failover groups and various account-object capabilities have additional edition requirements. &lt;a href="https://docs.snowflake.com/en/user-guide/account-replication-intro" rel="noopener noreferrer"&gt;Replication overview&lt;/a&gt;, &lt;a href="https://docs.snowflake.com/en/sql-reference/sql/create-replication-group" rel="noopener noreferrer"&gt;Replication schedules&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are evaluating replication for read access. Failover is a separate operating requirement and should not be introduced merely to make a read-only secondary writable for an integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The cost test that will decide the direction
&lt;/h2&gt;

&lt;p&gt;Snowflake replication adds transfer and service-compute charges, billed to the target account, plus target storage costs. Refresh frequency and changed data volume affect the total. The initial seed must be measured separately from steady-state refreshes. &lt;a href="https://docs.snowflake.com/en/user-guide/account-replication-cost" rel="noopener noreferrer"&gt;Snowflake replication cost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our comparison will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current path:
  source integration activity + transfer/storage requests
  + Fabric serving cost + operating effort

Azure replica path:
  replication transfer + replication compute + target storage
  + Azure-side integration/query cost + Fabric serving cost
  + operating effort
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are measurement categories, not a savings estimate. For external Iceberg storage, include the applicable cloud-storage bill as well as Snowflake and Fabric usage.&lt;/p&gt;

&lt;p&gt;My current direction is to test Azure replication. The former tests whether data location is driving unnecessary cost; the latter tests whether movement frequency is the larger problem. The decimal issue remains its own compatibility test.&lt;/p&gt;

&lt;p&gt;That is what I want the next installment to report: which option we tested, what improved, what it cost, and what still required a workaround.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>azure</category>
      <category>cloud</category>
      <category>database</category>
    </item>
    <item>
      <title>Moving Data Is Easier Than Moving Knowledge</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Mon, 07 Sep 2026 04:15:25 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/moving-data-is-easier-than-moving-knowledge-2co4</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/moving-data-is-easier-than-moving-knowledge-2co4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is Part 1 of a two-part technical series. &lt;a href="https://medium.com/@ghrupakkumar/how-to-use-ai-agents-to-build-ai-ready-data-products-c19ea4895102" rel="noopener noreferrer"&gt;Rupak's article on building AI-ready data products&lt;/a&gt; describes the larger destination: a machine-readable context layer, governed semantics, and engineering guardrails. This post tackles the migration problem that comes first—recovering legacy knowledge and turning it into grounded SQL. Part 2 will cover natural-language questions, governed execution, and explained answers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;All examples and names below are synthetic&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Moving data is often the easier part of an SAP-to-cloud migration. Moving the knowledge is harder.&lt;/p&gt;

&lt;p&gt;A pipeline can copy a table. It does not automatically carry forward why two tables are joined, which filter defines an open order, how technical statuses become business terms, or who owns the report.&lt;/p&gt;

&lt;p&gt;Without that context, a new platform can produce a technically valid answer that is wrong for the business.&lt;/p&gt;

&lt;h2&gt;
  
  
  A question the copied tables cannot answer
&lt;/h2&gt;

&lt;p&gt;Imagine an estate with more than 5,000 SAP HANA calculation views, hundreds of Qlik and Spotfire applications, and more than 2,500 cloud tables.&lt;/p&gt;

&lt;p&gt;A data engineer receives a simple request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rebuild our sales-order backlog dashboard. What existing logic should I preserve?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is scattered. A HANA calculation view may define the joins, filters, and open quantity. A Qlik application may map technical statuses and write a shared QVD. A Spotfire analysis may implement its own aging logic. Ownership and downstream usage may live in reporting metadata, while field-level dependencies live in a lineage graph.&lt;/p&gt;

&lt;p&gt;Qlik and Spotfire are &lt;strong&gt;parallel reporting platforms over HANA&lt;/strong&gt;. They are not sequential stages feeding one another; the same HANA model may support applications in either or both.&lt;/p&gt;

&lt;p&gt;The migration task is therefore not just code conversion. It is evidence discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the assistant builds context
&lt;/h2&gt;

&lt;p&gt;We call the user experience an &lt;strong&gt;Enterprise Data Discovery Assistant&lt;/strong&gt;. Under the hood, a bounded Snowflake Cortex Agent selects retrieval and analysis tools. “Agent” describes the architecture; “assistant” describes its read-only responsibility. It does not change source systems, deploy code, or promote pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Engineer question
       |
       v
Enterprise Data Discovery Assistant
       |
       +-- HANA repository code
       +-- Parallel BI artifacts
       |      +-- Qlik scripts and QVDs
       |      +-- Spotfire queries and metadata
       +-- Reporting metadata catalog
       +-- Schema, object, and column lineage
       |
       v
Recovered logic + ownership + dependencies
       |
       v
Grounded SQL draft -&amp;gt; data-engineer validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The four evidence layers play different roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HANA repository code&lt;/strong&gt; provides the calculation-view definition. Parsing indexed &lt;code&gt;.hdbcalculationview&lt;/code&gt; XML reveals sources, joins, filters, calculated columns, unions, and nested-view dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parallel BI artifacts&lt;/strong&gt; reveal reporting logic. Qlik contributes load queries, aliases, mappings, resident loads, and QVD flow. Spotfire contributes source queries, columns, and any calculations present in its indexed metadata or exports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;reporting metadata catalog&lt;/strong&gt; connects Qlik and Spotfire applications to HANA models, queries, fields, owners, paths, saved-query details, and comments. It adds operational context around the code; it does not replace the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema and lineage metadata&lt;/strong&gt; traces upstream sources, downstream consumers, object dependencies, and column paths, then helps map recovered logic to approved target objects.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these sources answer more than “What SQL should I write?” They can also answer: Who owns this dashboard? Which reports use this HANA view? Which QVDs feed the application? What could break if the model changes?&lt;/p&gt;

&lt;p&gt;Search locates the artifacts. Deterministic parsers extract their structure. The language model relates the findings to the engineer's question.&lt;/p&gt;

&lt;h2&gt;
  
  
  From a question to grounded SQL
&lt;/h2&gt;

&lt;p&gt;The experience should feel like a conversation, not a metadata report. The engineer asks one question; the assistant shows what it checked and returns an evidence-backed answer with SQL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flj15inqa1mx47pv00thg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flj15inqa1mx47pv00thg.gif" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Synthetic chat demonstration: no production data or internal interface is shown.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The response separates recovered facts from the proposed target design. In this example, the assistant verifies the HANA joins and filter, recovers the Qlik status mapping and QVD flow, identifies the owner, and finds downstream Qlik and Spotfire consumers before drafting SQL.&lt;/p&gt;

&lt;p&gt;Only then does the assistant propose a starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ordered_qty&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirmed_qty&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;open_quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;business_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales_order_item&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;status_map&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelled_flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SQL is useful because its origin and limitations are visible—not because it appeared quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grounded SQL is a handoff
&lt;/h2&gt;

&lt;p&gt;The draft is not a production pipeline. A data engineer still confirms the grain, compares results with the legacy report, maps the logic to canonical target names, and preserves null, fallback, and refresh behavior. Production work also requires incremental patterns such as CDC or watermarks, data-quality tests, security, documentation, owner approval, and CI/CD which was covered in Developer Toolkit.&lt;/p&gt;

&lt;p&gt;Trust comes from a few explicit boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer exact identifiers when available; use semantic search for discovery, then verify the selected objects.&lt;/li&gt;
&lt;li&gt;Parse large HANA XML and Qlik scripts deterministically.&lt;/li&gt;
&lt;li&gt;Separate &lt;strong&gt;verified evidence&lt;/strong&gt;, &lt;strong&gt;proposed design&lt;/strong&gt;, and &lt;strong&gt;missing evidence&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Keep generated code read-only until it passes normal engineering review and testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an expected Qlik or Spotfire expression is absent from the indexed sources, the assistant says so instead of reconstructing it from assumptions.&lt;/p&gt;

&lt;p&gt;The same human-in-the-loop pattern shaped the assistant itself. &lt;strong&gt;Snowflake CoCo&lt;/strong&gt;,supported the build-and-tune cycle by helping refine tool instructions, test prompts, failure analysis, and response behavior. Engineers decided which changes to adopt and validated the results.&lt;/p&gt;

&lt;p&gt;Moving data gives a new platform rows and columns. Moving knowledge preserves the meaning, ownership, and dependencies people rely on—and provides the context needed to build governed data products.&lt;/p&gt;

&lt;p&gt;Part 2 will follow a natural-language question through governed SQL execution and show how the final answer explains its sources and limitations.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>snowflake</category>
      <category>sap</category>
      <category>agents</category>
    </item>
    <item>
      <title>From Optimization to Protection: Adding a Security and Governance Agent to Your Snowflake Multi-Agent Team (Part 3)</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Thu, 09 Jul 2026 21:26:01 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/from-optimization-to-protection-adding-a-security-and-governance-agent-to-your-snowflake-369f</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/from-optimization-to-protection-adding-a-security-and-governance-agent-to-your-snowflake-369f</guid>
      <description>&lt;h1&gt;
  
  
  From Optimization to Protection: Adding a Security and Governance Agent to Your Snowflake Multi-Agent Team (Part 3)
&lt;/h1&gt;

&lt;p&gt;In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/ask-your-snowflake-account-anything-build-an-ai-admin-agent-with-cortex-github-copilot-1mk6"&gt;Part 1&lt;/a&gt;, we built an Admin Agent for usage and cost visibility.&lt;br&gt;
In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/from-one-agent-to-many-building-a-multi-agent-team-for-snowflake-administration-part-2-379d"&gt;Part 2&lt;/a&gt;, we added a Cost Optimizer Agent and an Orchestrator that routes questions to specialists.&lt;/p&gt;

&lt;p&gt;Now we close the loop with the third specialist: a &lt;strong&gt;Security and Governance Agent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This turns your assistant from "what happened" and "what to optimize" into a full team that also answers "what is risky right now".&lt;/p&gt;

&lt;p&gt;By the end of this post, you will have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Security and Governance Agent with focused security tools&lt;/li&gt;
&lt;li&gt;Security semantic views mapped to natural language&lt;/li&gt;
&lt;li&gt;Orchestrator routing across Admin, Cost Optimizer, and Security agents&lt;/li&gt;
&lt;li&gt;A practical triage workflow for failed logins, privilege risk, and unauthorized access&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why Add a Security Specialist?
&lt;/h2&gt;

&lt;p&gt;The first two agents are strong for operations and spend, but security requires a different lens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access control and role hygiene&lt;/li&gt;
&lt;li&gt;Failed login patterns and anomaly detection&lt;/li&gt;
&lt;li&gt;Unauthorized access attempts&lt;/li&gt;
&lt;li&gt;Inactive users with active privileges&lt;/li&gt;
&lt;li&gt;Compliance-friendly audit summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Could one large agent do everything? Sometimes. But specialized agents are easier to maintain, safer to evolve, and easier to test.&lt;/p&gt;
&lt;h2&gt;
  
  
  Final Team Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question (natural language)
        |
  Orchestrator Agent
   /      |        \
Admin   Cost     Security
Agent  Optimizer Governance
                 Agent
        \    |    /
      Unified Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Role of each specialist
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Admin Agent: usage, credits, storage, operational metrics&lt;/li&gt;
&lt;li&gt;Cost Optimizer Agent: idle compute, rightsizing, optimization opportunities&lt;/li&gt;
&lt;li&gt;Security and Governance Agent: roles, privileges, failed logins, unauthorized access, audits&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Security Pattern (Same Foundation as Parts 1 and 2)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Base Views
&lt;/h3&gt;

&lt;p&gt;Create security-focused views over &lt;code&gt;SNOWFLAKE.ACCOUNT_USAGE&lt;/code&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Role hierarchy and privilege grants&lt;/li&gt;
&lt;li&gt;Failed login attempts and anomaly severity&lt;/li&gt;
&lt;li&gt;Excessive or unused privileged access&lt;/li&gt;
&lt;li&gt;Unauthorized access attempts&lt;/li&gt;
&lt;li&gt;User and role audit summaries&lt;/li&gt;
&lt;li&gt;Network policy activity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sql/08_create_security_governance_views.sql&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 2: Semantic Views
&lt;/h3&gt;

&lt;p&gt;Map these views into natural language dimensions, facts, and metrics so Cortex Analyst can reason over them.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SV_LOGIN_ANOMALIES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SV_EXCESSIVE_PRIVILEGES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SV_UNAUTHORIZED_ACCESS_ATTEMPTS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SV_USER_ROLE_AUDIT&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sql/09_create_security_governance_semantic_views.sql&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 3: Create the Security Agent
&lt;/h3&gt;

&lt;p&gt;Define a dedicated agent with explicit analyst tools for each security domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;AGENT&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;APP_DB&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;APP_SCHEMA&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SECURITY_AGENT_NAME&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;COMMENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Security and Governance agent for access control and compliance'&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SPECIFICATION&lt;/span&gt;
  &lt;span class="err"&gt;$$&lt;/span&gt;
  &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"You are a Security and Governance assistant..."&lt;/span&gt;
    &lt;span class="n"&gt;orchestration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"Route role hierarchy questions to RoleHierarchyAnalyst; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;
                   privilege grants to PrivilegeGrantAnalyst; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;
                   failed logins to FailedLoginAnalyst; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;
                   login anomalies to LoginAnomalyDetector..."&lt;/span&gt;

  &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"RoleHierarchyAnalyst"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"PrivilegeGrantAnalyst"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"FailedLoginAnalyst"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"LoginAnomalyDetector"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"ExcessivePrivilegeAnalyst"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"UnauthorizedAccessAnalyst"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"cortex_analyst_text_to_sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"UserAuditAnalyst"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementation file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sql/11_create_security_governance_agent.sql&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Route Through the Orchestrator
&lt;/h3&gt;

&lt;p&gt;Your orchestrator now includes &lt;code&gt;SecurityAgent&lt;/code&gt; as a first-class route target.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- in sql/12_create_orchestrator_agent.sql&lt;/span&gt;
&lt;span class="c1"&gt;-- Questions about security, roles, privileges, failed logins, unauthorized access -&amp;gt; SecurityAgent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can also fan out to multiple agents for blended questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 1: Security-Only Routing
&lt;/h2&gt;

&lt;p&gt;Question:&lt;br&gt;
"Are there suspicious login failures in the last 7 days?"&lt;/p&gt;

&lt;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Orchestrator classifies this as security monitoring&lt;/li&gt;
&lt;li&gt;Routes to SecurityAgent&lt;/li&gt;
&lt;li&gt;SecurityAgent uses &lt;code&gt;SV_LOGIN_ANOMALIES&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Returns severity-based findings and recommendations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Login Anomaly Summary (Last 7 Days)

Critical: 2 users with &amp;gt;= 10 failed attempts/hour
High: 5 users with 5-9 failed attempts/hour
Pattern: Multiple failed attempts from distinct IPs for USER_X

Recommendation:
- Lock and verify impacted accounts
- Enforce MFA re-registration for affected users
- Review network policy and source IP ranges
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example 2: Privilege Governance
&lt;/h2&gt;

&lt;p&gt;Question:&lt;br&gt;
"Which users have ACCOUNTADMIN or SECURITYADMIN but low recent usage?"&lt;/p&gt;

&lt;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Routed to SecurityAgent&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;SV_EXCESSIVE_PRIVILEGES&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Identifies high-risk assignments with low usage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Excessive Privilege Findings

Users flagged: 4
Critical: 2 users with no privileged role usage in 60+ days
High: 2 users with &amp;lt; 5 privileged queries in 90 days

Recommendation:
- Revoke unused privileged grants
- Replace standing privilege with just-in-time elevation
- Document business justification for remaining elevated users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example 3: Cross-Agent Investigation
&lt;/h2&gt;

&lt;p&gt;Question:&lt;br&gt;
"Why are costs up and is there any security risk around this?"&lt;/p&gt;

&lt;p&gt;What happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Orchestrator identifies multi-domain intent&lt;/li&gt;
&lt;li&gt;Routes to AdminAgent + CostOptimizerAgent + SecurityAgent&lt;/li&gt;
&lt;li&gt;Aggregates usage, optimization, and security posture into one answer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Integrated Account Assessment

Operations (Admin Agent)
- Compute credits up 18% month-over-month
- Growth concentrated in two ETL warehouses

Optimization (Cost Optimizer Agent)
- Idle percentage &amp;gt; 60% on one ETL warehouse
- Suggested AUTO_SUSPEND change could reduce waste materially

Security (Security Agent)
- One privileged user inactive but still assigned elevated role
- Increased failed login attempts from multiple IPs for two accounts

Priority Actions
1) Apply warehouse auto-suspend tuning
2) Review elevated role assignments and revoke unused grants
3) Investigate failed-login anomalies and tighten network controls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment Sequence
&lt;/h2&gt;

&lt;p&gt;If you already deployed Parts 1 and 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1) Security views&lt;/span&gt;
sql/08_create_security_governance_views.sql

&lt;span class="c"&gt;# 2) Security semantic layer&lt;/span&gt;
sql/09_create_security_governance_semantic_views.sql

&lt;span class="c"&gt;# 3) Security agent&lt;/span&gt;
sql/11_create_security_governance_agent.sql

&lt;span class="c"&gt;# 4) Orchestrator (includes SecurityAgent routing)&lt;/span&gt;
sql/12_create_orchestrator_agent.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Queries
&lt;/h2&gt;

&lt;p&gt;Direct test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORTEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DATA_AGENT_RUN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'&amp;lt;APP_DB&amp;gt;.&amp;lt;APP_SCHEMA&amp;gt;.&amp;lt;SECURITY_AGENT_NAME&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'{"messages":[{"role":"user","content":[{"type":"text","text":"Show failed login anomalies by severity"}]}]}'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Orchestrated test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORTEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DATA_AGENT_RUN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'&amp;lt;APP_DB&amp;gt;.&amp;lt;APP_SCHEMA&amp;gt;.&amp;lt;ORCHESTRATOR_AGENT_NAME&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'{"messages":[{"role":"user","content":[{"type":"text","text":"Analyze account risk: costs, idle warehouses, and failed logins"}]}]}'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practical Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SNOWFLAKE.ACCOUNT_USAGE&lt;/code&gt; views have latency. For near-real-time incident response, combine this with event-driven telemetry.&lt;/li&gt;
&lt;li&gt;Keep role and privilege reviews on a recurring schedule (monthly or quarterly depending on policy).&lt;/li&gt;
&lt;li&gt;Use placeholders and environment-specific grants consistently:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;APP_DB&amp;gt;.&amp;lt;APP_SCHEMA&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;EXEC_WAREHOUSE&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;ADMIN_ROLE&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;DEVELOPER_ROLE&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What We Have Now
&lt;/h2&gt;

&lt;p&gt;After Part 3, you have a complete multi-agent Snowflake administration team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin Agent for operational visibility&lt;/li&gt;
&lt;li&gt;Cost Optimizer Agent for efficiency and savings&lt;/li&gt;
&lt;li&gt;Security and Governance Agent for risk and compliance&lt;/li&gt;
&lt;li&gt;Orchestrator Agent for seamless, natural-language routing across all three&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where multi-agent design pays off: each specialist stays focused, and users still ask one simple question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Security base views: &lt;code&gt;sql/08_create_security_governance_views.sql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Security semantic views: &lt;code&gt;sql/09_create_security_governance_semantic_views.sql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Security agent: &lt;code&gt;sql/11_create_security_governance_agent.sql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Orchestrator routing: &lt;code&gt;sql/12_create_orchestrator_agent.sql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Security skill guidance: &lt;code&gt;skills/security-governance/SKILL.md&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code Repository
&lt;/h2&gt;

&lt;p&gt;Complete implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/LALITHASWAROOPK/agent_snowflake_admin" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/LALITHASWAROOPK/agent_snowflake_admin/tree/main/sql" rel="noopener noreferrer"&gt;SQL Folder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/LALITHASWAROOPK/agent_snowflake_admin/blob/main/mcp/server.py" rel="noopener noreferrer"&gt;MCP Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Part 1 gave us visibility.&lt;br&gt;
Part 2 gave us optimization.&lt;br&gt;
Part 3 gives us governance and risk control.&lt;/p&gt;

&lt;p&gt;Same architecture pattern, broader coverage:&lt;br&gt;
&lt;strong&gt;Views -&amp;gt; Semantic Views -&amp;gt; Specialist Agent -&amp;gt; Orchestrator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add automated alerting and ticket creation workflows&lt;/li&gt;
&lt;li&gt;Add remediation playbooks per risk severity&lt;/li&gt;
&lt;li&gt;Add environment-level policy checks for continuous compliance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or feedback? Drop a comment below.&lt;/p&gt;

&lt;p&gt;Part 1: &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/ask-your-snowflake-account-anything-build-an-ai-admin-agent-with-cortex-github-copilot-1mk6"&gt;Ask Your Snowflake Account Anything - Build an AI Admin Agent&lt;/a&gt;&lt;br&gt;
Part 2: &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/from-one-agent-to-many-building-a-multi-agent-team-for-snowflake-administration-part-2-379d"&gt;From One Agent to Many - Building a Multi-Agent Team&lt;/a&gt;&lt;br&gt;
Part 3: Security and Governance Agent (this post)&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>ai</category>
      <category>security</category>
      <category>governance</category>
    </item>
    <item>
      <title>Mirroring Snowflake Iceberg into Microsoft Fabric: The Gotchas - Part 1:</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Thu, 11 Jun 2026 22:53:42 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/what-i-learned-running-snowflake-iceberg-mirroring-in-microsoft-fabric-and-debugging-real-1151</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/what-i-learned-running-snowflake-iceberg-mirroring-in-microsoft-fabric-and-debugging-real-1151</guid>
      <description>&lt;p&gt;&lt;strong&gt;Previously:&lt;/strong&gt; In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/setting-up-snowflake-power-bi-connectivity-with-azure-ad-sso-and-auto-provisioning-1gda"&gt;Power BI Connectivity with Azure AD&lt;/a&gt;, I walked through enabling end-to-end SSO from Power BI to Snowflake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this post:&lt;/strong&gt; A hands-on implementation log for Snowflake mirroring (including Iceberg tables) in Microsoft Fabric — what official docs confirm, what broke in production, and the runbook that now works. If you're seeing differences between Mirroring Status and SQL Analytics Endpoint, this should help.&lt;/p&gt;

&lt;p&gt;Our goal was to serve analysts from Fabric's shared OneLake layer instead of having every downstream workflow repeatedly query Snowflake directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fabric Mirroring for Snowflake continuously replicates source data into OneLake.&lt;/li&gt;
&lt;li&gt;For Iceberg tables, Fabric replicates metadata to OneLake via shortcuts to the underlying storage and OneLake exposes data in Delta format for Fabric workloads.&lt;/li&gt;
&lt;li&gt;SQL Analytics Endpoint is read-only and can show type/metadata limitations even when mirroring is healthy.&lt;/li&gt;
&lt;li&gt;New or changed tables can take time to appear consistently in SQL Analytics Endpoint because replication and SQL metadata sync are separate steps.&lt;/li&gt;
&lt;li&gt;Connection configuration accuracy matters (especially warehouse identity and permissions).&lt;/li&gt;
&lt;li&gt;Not all Snowflake table types are eligible for mirroring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1o73nrm7iqo1g8vlpsn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1o73nrm7iqo1g8vlpsn.png" alt=" " width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I validated against official Microsoft docs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. How mirroring works for Snowflake in Fabric
&lt;/h3&gt;

&lt;p&gt;From Microsoft documentation, mirroring creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A mirrored database item in Fabric&lt;/li&gt;
&lt;li&gt;A SQL Analytics Endpoint for querying the mirrored data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Snowflake sources specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iceberg table metadata is replicated into OneLake using shortcuts to the storage that contains those Iceberg tables.&lt;/li&gt;
&lt;li&gt;OneLake automatically converts those Iceberg tables to Delta Lake formatted tables for Fabric workloads.&lt;/li&gt;
&lt;li&gt;Managed table data is replicated into OneLake and converted to analytics-ready format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means consumption in Fabric is against replicated data in OneLake, not a live pass-through to Snowflake. When users say "SQL endpoint looks wrong," I now split diagnosis into replication-plane checks vs SQL metadata-plane checks.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. SQL Analytics Endpoint is read-only and has data type/materialization limits
&lt;/h3&gt;

&lt;p&gt;Microsoft documents that SQL Analytics Endpoint uses the same engine family and constraints as Fabric Warehouse for persisted/object materialized types. In practice this explains why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mirroring can be healthy&lt;/li&gt;
&lt;li&gt;But some columns can be missing/truncated/not surfaced as expected in SQL Analytics Endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially important when source types do not map cleanly to endpoint-supported persisted types.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Snowflake mirroring limitations that matter in design
&lt;/h3&gt;

&lt;p&gt;Official Snowflake mirroring limitations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regular/native table replication focus (not all table categories are supported)&lt;/li&gt;
&lt;li&gt;A current mirrored table-count limit per mirrored database&lt;/li&gt;
&lt;li&gt;Schema-change replication nuances (some schema changes need data changes to trigger propagation)&lt;/li&gt;
&lt;li&gt;Backoff behavior when source tables are idle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Cost and serving model
&lt;/h3&gt;

&lt;p&gt;From Microsoft guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fabric query workloads (SQL, Spark, BI) consume Fabric capacity.&lt;/li&gt;
&lt;li&gt;Snowflake compute can be incurred for data-change reads during replication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a one-time load model; mirroring is continuous synchronization. Total cost depends on change volume, replication activity, and Fabric-side query demand.&lt;/p&gt;

&lt;p&gt;Why we still chose it: it reduced repeated direct-query pressure from many downstream users and gave us a shared serving layer across SQL, Spark, and BI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Incident Log: What broke and what fixed it
&lt;/h2&gt;

&lt;p&gt;Simple-looking errors often had multiple causes: connection identity, permissions, metadata timing, and endpoint type handling. The key discipline was separating what docs explicitly confirm from support heuristics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ticket #1–2: Connection and warehouse errors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Symptom
&lt;/h3&gt;

&lt;p&gt;Mirroring refresh failed with warehouse-not-found / not-authorized style errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we found
&lt;/h3&gt;

&lt;p&gt;The configured warehouse identity in Fabric connection did not align with the warehouse defined/accessible in Snowflake for the mirroring principal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Stop replication&lt;/li&gt;
&lt;li&gt;Recreate the Snowflake connection in Fabric&lt;/li&gt;
&lt;li&gt;Re-enter server/warehouse carefully&lt;/li&gt;
&lt;li&gt;Ensure the principal has warehouse usage + required mirroring privileges&lt;/li&gt;
&lt;li&gt;Rebind mirrored database to the corrected connection&lt;/li&gt;
&lt;li&gt;Restart replication and monitor&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Important precision
&lt;/h3&gt;

&lt;p&gt;Microsoft docs explicitly tell you to normalize server entry format (for example remove protocol and use lowercase server host format in setup guidance). For warehouse value handling, my operational practice is to treat it as an exact identifier and validate both existence and authorization before restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau0lox9pr0wd17vt6s83.png" alt=" " width="340" height="646"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Ticket #3: Mirroring successful, but SQL Analytics Endpoint shows column issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Symptom
&lt;/h3&gt;

&lt;p&gt;Mirroring status looked healthy, but querying via SQL Analytics Endpoint showed warnings or incomplete column exposure for some tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root cause pattern
&lt;/h3&gt;

&lt;p&gt;Endpoint-level SQL metadata and type mapping did not always represent source columns as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this happens
&lt;/h3&gt;

&lt;p&gt;This behavior is aligned with documented SQL Analytics Endpoint limitations and data type mapping constraints. Mirroring into OneLake can still be successful while SQL endpoint representation is partially constrained.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2731uu15u8886jw60st.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2731uu15u8886jw60st.png" alt=" " width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Validate data in OneLake directly (for example via Spark) when endpoint output looks suspicious&lt;/li&gt;
&lt;li&gt;Cast unsupported/problematic source columns to endpoint-friendly types in curated tables/views&lt;/li&gt;
&lt;li&gt;Use SQL endpoint refresh and metadata sync checks where relevant&lt;/li&gt;
&lt;li&gt;For critical workloads, create curated serving layers specifically for endpoint compatibility&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture model that helped me
&lt;/h2&gt;

&lt;p&gt;Think about Snowflake mirroring in Fabric as three planes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replication plane&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Movement/sync of data and metadata into OneLake&lt;/li&gt;
&lt;li&gt;Includes Iceberg metadata handling + managed table replication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Storage/open format plane&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data persisted in OneLake with Delta interoperability for Fabric engines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Serving plane&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL Analytics Endpoint as read-only, T-SQL-friendly surface&lt;/li&gt;
&lt;li&gt;Useful, but not equivalent to full source-type fidelity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most confusion in operations comes from mixing plane #1 health with plane #3 usability.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdcpccb3je8m3za4npqb.png" alt=" " width="322" height="706"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  My production runbook now
&lt;/h2&gt;

&lt;p&gt;Before enabling mirroring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate Snowflake privileges for mirroring principal (&lt;code&gt;CREATE STREAM&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;SHOW TABLES&lt;/code&gt;, &lt;code&gt;DESCRIBE TABLES&lt;/code&gt; per Microsoft/Snowflake guidance)&lt;/li&gt;
&lt;li&gt;Validate server entry format and connection parameters&lt;/li&gt;
&lt;li&gt;Validate warehouse existence and effective permissions for principal&lt;/li&gt;
&lt;li&gt;Identify which tables are regular/native and eligible&lt;/li&gt;
&lt;li&gt;For Iceberg, confirm object store (e.g. S3) permissions up front: the mirroring principal needs read access to the underlying bucket where Iceberg data files are stored, not just Snowflake-level privileges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After enabling mirroring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor table-level replication status and last refresh&lt;/li&gt;
&lt;li&gt;Distinguish OneLake landing success from SQL endpoint metadata availability&lt;/li&gt;
&lt;li&gt;Add smoke tests in both Spark and SQL endpoint&lt;/li&gt;
&lt;li&gt;Track Snowflake compute impact during replication windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When troubleshooting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If connection/auth errors: recreate connection cleanly, verify identifiers and grants&lt;/li&gt;
&lt;li&gt;If missing columns in endpoint: check SQL endpoint limitations and data type mappings, then curate/cast&lt;/li&gt;
&lt;li&gt;If schema changes not reflected: trigger/verify data change propagation and refresh metadata&lt;/li&gt;
&lt;li&gt;If large-table updates are massive: consider stop/start strategy per documented performance guidance&lt;/li&gt;
&lt;li&gt;If newly added tables don't appear in SQL endpoint: wait for initial replication to complete, validate data landed in OneLake/Spark first, then trigger endpoint metadata refresh — endpoint availability is near-real-time, not instant&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common misconceptions I had (now corrected)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Misconception 1: "Mirroring success means SQL endpoint is guaranteed complete"
&lt;/h3&gt;

&lt;p&gt;Not always true. Replication and endpoint representation are related but not identical concerns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Misconception 2: "All Snowflake table variants mirror the same way"
&lt;/h3&gt;

&lt;p&gt;No. Supported categories are explicitly constrained; design with documented limitations first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Misconception 3: "If it says queried live, Fabric is directly querying Snowflake"
&lt;/h3&gt;

&lt;p&gt;Operationally, Fabric mirroring is a replication model into OneLake with Fabric-side query consumption.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fabric mirroring for Snowflake Iceberg works well when you separate replication health from SQL endpoint behavior.&lt;/li&gt;
&lt;li&gt;SQL Analytics Endpoint issues do not always mean replication failure.&lt;/li&gt;
&lt;li&gt;Connection hygiene (especially warehouse + auth correctness) prevents a surprising number of incidents.&lt;/li&gt;
&lt;li&gt;For production, design a compatibility layer for endpoint consumption instead of relying on raw source type fidelity.&lt;/li&gt;
&lt;li&gt;This was genuinely painful to implement end-to-end, but the pain forced a better operating model and a sharper troubleshooting discipline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of those integrations where knowing the documented constraints upfront saves far more time than reactive firefighting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Neutrality and scope disclosure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This post reflects my hands-on implementation experience and support-ticket learnings.&lt;/li&gt;
&lt;li&gt;It is not sponsored content and not intended as a blanket endorsement of any product.&lt;/li&gt;
&lt;li&gt;Platform behavior, latency, and cost outcomes can vary by workload shape, table volume, schema patterns, and operating model.&lt;/li&gt;
&lt;li&gt;The intent is practical: help others avoid common mistakes by combining official documentation with real incident patterns.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Sources (official)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mirroring Snowflake in Microsoft Fabric:
&lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/snowflake" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/mirroring/snowflake&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tutorial: Configure mirrored Snowflake:
&lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-tutorial" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Snowflake mirroring limitations:
&lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-limitations" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-limitations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Troubleshoot mirrored databases:
&lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/troubleshooting" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/mirroring/troubleshooting&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Snowflake mirroring FAQ:
&lt;a href="https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-mirroring-faq" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/mirroring/snowflake-mirroring-faq&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SQL Analytics Endpoint limitations:
&lt;a href="https://learn.microsoft.com/en-us/fabric/data-engineering/lakehouse-sql-analytics-endpoint#limitations" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/data-engineering/lakehouse-sql-analytics-endpoint#limitations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Data types in Fabric Data Warehouse / SQL endpoint mapping:
&lt;a href="https://learn.microsoft.com/en-us/fabric/data-warehouse/data-types" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/fabric/data-warehouse/data-types&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoft</category>
      <category>dbt</category>
      <category>snowflake</category>
      <category>iceberg</category>
    </item>
    <item>
      <title>Evaluating Adaptive Warehouses for ETL: Why We Reverted to Standard</title>
      <dc:creator>Krishna Tangudu</dc:creator>
      <pubDate>Wed, 27 May 2026 14:13:16 +0000</pubDate>
      <link>https://dev.to/swaroop_krishna_e2f4b83b2/evaluating-adaptive-warehouses-for-etl-why-we-reverted-to-standard-239f</link>
      <guid>https://dev.to/swaroop_krishna_e2f4b83b2/evaluating-adaptive-warehouses-for-etl-why-we-reverted-to-standard-239f</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/how-a-simple-warehouse-resize-saved-us-11-in-daily-credits-while-boosting-performance-2753"&gt;our last post&lt;/a&gt; we showed how upsizing a Bronze ETL warehouse from XSmall to Small reduced daily credits by 11%. This post examines a different question: does Snowflake's Adaptive warehouse model (preview) improve on what a well-tuned Standard warehouse already delivers?&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://docs.snowflake.com/en/user-guide/warehouses-adaptive" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;docs.snowflake.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;For our Silver layer workload, the answer was no.&lt;/p&gt;




&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;WH_ETL_SILVER_01&lt;/code&gt; is our dedicated warehouse for Silver layer data loads — long-running transformation queries at a steady, predictable rate of ~2,000 queries/day. On May 12, 2026 we migrated it from Standard to Adaptive to evaluate the new model under production conditions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Standard Config&lt;/th&gt;
&lt;th&gt;Adaptive Config&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;STANDARD&lt;/td&gt;
&lt;td&gt;ADAPTIVE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small&lt;/td&gt;
&lt;td&gt;Max: Small&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency/Burst&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MAX_CLUSTER_COUNT = 3&lt;/td&gt;
&lt;td&gt;QUERY_THROUGHPUT_MULTIPLIER = 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto-Suspend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;60 seconds&lt;/td&gt;
&lt;td&gt;Managed by Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After 16 days of production data, we reverted.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Queuing That Wouldn't Stop Growing
&lt;/h2&gt;

&lt;p&gt;Within the first week on adaptive, we started seeing queries queue. By week three, the queue rate had grown to 3.4% — and still climbing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Period&lt;/th&gt;
&lt;th&gt;Config&lt;/th&gt;
&lt;th&gt;Queue Rate&lt;/th&gt;
&lt;th&gt;Avg Wait (queued)&lt;/th&gt;
&lt;th&gt;Max Wait&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apr 1 – May 11&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;~0%&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 12–15&lt;/td&gt;
&lt;td&gt;Adaptive Week 1&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;td&gt;~10s&lt;/td&gt;
&lt;td&gt;102s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 16–22&lt;/td&gt;
&lt;td&gt;Adaptive Week 2&lt;/td&gt;
&lt;td&gt;2.5%&lt;/td&gt;
&lt;td&gt;80–98s&lt;/td&gt;
&lt;td&gt;490s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 23–27&lt;/td&gt;
&lt;td&gt;Adaptive Week 3&lt;/td&gt;
&lt;td&gt;3.4%&lt;/td&gt;
&lt;td&gt;65–80s&lt;/td&gt;
&lt;td&gt;508s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Under Standard with the same ~2,000 queries/day load, queueing was effectively zero. Two queries queued on the worst day, for 100ms each.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fair Comparison: Matched Load Windows
&lt;/h2&gt;

&lt;p&gt;To avoid skewing results by different query volumes, we compared periods with equivalent daily throughput.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard baseline&lt;/strong&gt;: May 1–11 (11 days, avg 2,151 queries/day)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive comparison&lt;/strong&gt;: May 12–14, 18–22, 25–26 (10 days, avg 2,060 queries/day)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Query Performance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Standard&lt;/th&gt;
&lt;th&gt;Adaptive&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg Elapsed Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;19.9s&lt;/td&gt;
&lt;td&gt;22.8s&lt;/td&gt;
&lt;td&gt;+15% slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Median Elapsed Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;256ms&lt;/td&gt;
&lt;td&gt;267ms&lt;/td&gt;
&lt;td&gt;+4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P95 Elapsed Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14.1s&lt;/td&gt;
&lt;td&gt;20.4s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+45% slower&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg Execution Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;19.6s&lt;/td&gt;
&lt;td&gt;21.0s&lt;/td&gt;
&lt;td&gt;+7% slower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Standard&lt;/th&gt;
&lt;th&gt;Adaptive&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg Daily Credits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;15.2&lt;/td&gt;
&lt;td&gt;19.1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+26% more expensive&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credits Per Query&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.0071&lt;/td&gt;
&lt;td&gt;0.0093&lt;/td&gt;
&lt;td&gt;+31% more expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Queuing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Standard&lt;/th&gt;
&lt;th&gt;Adaptive&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queue Rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;td&gt;2.2% (growing)&lt;/td&gt;
&lt;td&gt;+7x worse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg Queue Wait&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.3ms&lt;/td&gt;
&lt;td&gt;1,543ms&lt;/td&gt;
&lt;td&gt;+46,000% worse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How We Measured
&lt;/h2&gt;

&lt;p&gt;We used the same &lt;code&gt;ACCOUNT_USAGE&lt;/code&gt; views from the previous post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query Performance (Matched Windows)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2026-05-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2026-05-11'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Standard'&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Adaptive'&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_queries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_elapsed_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_elapsed_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MEDIAN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_elapsed_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;median_elapsed_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PERCENTILE_CONT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;WITHIN&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_elapsed_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p95_elapsed_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queued_overload_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_queued_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COUNT_IF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queued_overload_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                        &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;queue_rate_pct&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACCOUNT_USAGE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QUERY_HISTORY&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;warehouse_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'WH_ETL_SILVER_01'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2026-05-01'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Credit Consumption
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2026-05-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2026-05-11'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Standard'&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Adaptive'&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credits_used_compute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_daily_credits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credits_used_compute&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                      &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_credits&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACCOUNT_USAGE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WAREHOUSE_METERING_HISTORY&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;warehouse_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'WH_ETL_SILVER_01'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2026-05-01'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Adaptive Doesn't Fit This Workload
&lt;/h2&gt;

&lt;p&gt;Adaptive warehouses are designed for &lt;strong&gt;variable, unpredictable workloads&lt;/strong&gt; — dashboards, ad-hoc analytics, mixed query sizes. Our Silver pipeline is the opposite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Steady ~2,000 queries/day&lt;/li&gt;
&lt;li&gt;Predictable concurrency (same pipeline, same schedule)&lt;/li&gt;
&lt;li&gt;Long-running transformation queries — not short, bursty ones&lt;/li&gt;
&lt;li&gt;Consistent data volumes day-to-day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;QUERY_THROUGHPUT_MULTIPLIER = 3&lt;/code&gt; cap was insufficient for peak concurrency windows. The adaptive model's dynamic scaling overhead — which adds value when workloads are unpredictable — just introduced latency without benefit here. The Standard config's fixed multi-cluster model handled this workload profile cleanly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommendation: Revert to Standard
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;WAREHOUSE&lt;/span&gt; &lt;span class="n"&gt;WH_ETL_SILVER_01&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;WAREHOUSE_TYPE&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'STANDARD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;WAREHOUSE_SIZE&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SMALL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MAX_CLUSTER_COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SCALING_POLICY&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'STANDARD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;AUTO_SUSPEND&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected impact after reverting:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eliminate queuing (proven 0% queue rate at this load level)&lt;/li&gt;
&lt;li&gt;Save ~26% on daily credits&lt;/li&gt;
&lt;li&gt;Reduce P95 latency by 45%&lt;/li&gt;
&lt;li&gt;Restore predictable, consistent performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  If You Must Stay on Adaptive
&lt;/h3&gt;

&lt;p&gt;Snowflake's official guidance is clear: &lt;em&gt;increase &lt;code&gt;QUERY_THROUGHPUT_MULTIPLIER&lt;/code&gt; to reduce queuing&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;WAREHOUSE&lt;/span&gt; &lt;span class="n"&gt;WH_ETL_SILVER_01&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;QUERY_THROUGHPUT_MULTIPLIER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would likely resolve the queuing issue. However, for a &lt;strong&gt;predictable, steady-state ETL workload&lt;/strong&gt;, this approach adds operational complexity without strategic benefit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're now tuning a multiplier instead of configuring a fixed cluster count&lt;/li&gt;
&lt;li&gt;Higher throughput capacity may increase peak concurrent spend, requiring new cost monitoring&lt;/li&gt;
&lt;li&gt;For a pipeline that runs the same way every day, the adaptive model's flexibility adds overhead without value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; Adaptive's tuning knobs are powerful for variable workloads. For predictable ETL, Standard's simplicity wins operationally — and financially.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive is not universally better&lt;/strong&gt; — it adds overhead that only pays off for variable, unpredictable workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always compare at matched load&lt;/strong&gt; — raw averages across different query volumes are misleading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queuing is the canary&lt;/strong&gt; — a worsening queue rate on adaptive signals a workload mismatch, not just an under-tuned multiplier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard multi-cluster remains the better fit&lt;/strong&gt; for batch/ETL pipelines with consistent concurrency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserve adaptive for variable workloads&lt;/strong&gt; — ad-hoc analytics or environments where query volume swings 5–10x daily.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Closing Thought
&lt;/h2&gt;

&lt;p&gt;The lesson from &lt;a href="https://dev.to/swaroop_krishna_e2f4b83b2/how-a-simple-warehouse-resize-saved-us-11-in-daily-credits-while-boosting-performance-2753"&gt;post &lt;/a&gt;was: in some workloads, bigger can be cheaper. The lesson here is the inverse: newer is not always better. The right warehouse type depends on workload shape — steady pipelines and dynamic workloads have fundamentally different resource patterns, and Snowflake's warehouse models reflect that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclosure and Scope
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Results reflect our specific environment and workload profile; outcomes may vary.&lt;/li&gt;
&lt;li&gt;Analysis period: May 1–27, 2026 | Warehouse: &lt;code&gt;WH_ETL_SILVER_01&lt;/code&gt; | Workload: Silver layer production loads&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This analysis used Snowflake built-in &lt;code&gt;ACCOUNT_USAGE&lt;/code&gt; views only. No third-party monitoring stack required.&lt;/p&gt;

</description>
      <category>snowflake</category>
      <category>adaptive</category>
      <category>warehouse</category>
      <category>workload</category>
    </item>
  </channel>
</rss>
