<?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: Arvind Toorpu</title>
    <description>The latest articles on DEV Community by Arvind Toorpu (@arvind_toorpu).</description>
    <link>https://dev.to/arvind_toorpu</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%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png</url>
      <title>DEV Community: Arvind Toorpu</title>
      <link>https://dev.to/arvind_toorpu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arvind_toorpu"/>
    <language>en</language>
    <item>
      <title>SQL Server In-Memory OLTP: When Memory-Optimized Tables Actually Pay Off</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:53:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/sql-server-in-memory-oltp-when-memory-optimized-tables-actually-pay-off-3nee</link>
      <guid>https://dev.to/arvind_toorpu/sql-server-in-memory-oltp-when-memory-optimized-tables-actually-pay-off-3nee</guid>
      <description>&lt;p&gt;A client of mine had a session-state table taking 40,000 writes a minute during a flash sale, and every single one of them was queuing behind PAGELATCH_EX waits on the last page of a clustered index. Adding more CPU didn't help. Bigger disks didn't help. The bottleneck wasn't I/O, it was threads fighting over the same in-memory page structures. That's the exact scenario memory-optimized tables were built for, and it's also where I've seen people reach for them when the actual problem is a bad index design that In-Memory OLTP will just make expensive to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What memory-optimized tables actually are
&lt;/h2&gt;

&lt;p&gt;A memory-optimized table is a normal-looking &lt;code&gt;CREATE TABLE&lt;/code&gt; with &lt;code&gt;WITH (MEMORY_OPTIMIZED=ON)&lt;/code&gt; tacked on. Rows live in memory as the primary copy, not as a disk-page cache. There's no buffer pool, no page latching, no lock manager in the traditional sense. Concurrency is handled with row versioning and optimistic multi-version concurrency control, so readers never block writers and writers don't block each other on the same row unless they're actually touching the same row version.&lt;/p&gt;

&lt;p&gt;You still get durability by default: a checkpoint file pair on disk and transaction log records mean a memory-optimized table survives a restart just like a regular one, unless you explicitly opt out with &lt;code&gt;DURABILITY=SCHEMA_ONLY&lt;/code&gt; for things like staging tables or session state where losing the data on restart is fine.&lt;/p&gt;

&lt;p&gt;The performance case according to Microsoft's own docs is specifically latch contention, lock contention, and CPU overhead from the interpreter, not "make my queries faster" in general. If your workload isn't hitting one of those three, this feature won't do much for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The how-to
&lt;/h2&gt;

&lt;p&gt;Here's a durable memory-optimized table with a hash index for point lookups and a range index for the historical query pattern:&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;TABLE&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ShoppingCart&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ShoppingCartId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
        &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;NONCLUSTERED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
        &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;ix_UserId&lt;/span&gt; &lt;span class="n"&gt;NONCLUSTERED&lt;/span&gt;
        &lt;span class="n"&gt;HASH&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BUCKET_COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;CreatedDate&lt;/span&gt; &lt;span class="n"&gt;DATETIME2&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TotalPrice&lt;/span&gt; &lt;span class="n"&gt;MONEY&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MEMORY_OPTIMIZED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DURABILITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SCHEMA_AND_DATA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a non-durable session table where you're fine losing rows on a restart, which is the classic ASP.NET session state use case:&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;TABLE&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserSession&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;SessionId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
        &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;NONCLUSTERED&lt;/span&gt; &lt;span class="n"&gt;HASH&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BUCKET_COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CreatedDate&lt;/span&gt; &lt;span class="n"&gt;DATETIME2&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ShoppingCartId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MEMORY_OPTIMIZED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DURABILITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SCHEMA_ONLY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you create the table, the database needs a memory-optimized filegroup:&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;DATABASE&lt;/span&gt; &lt;span class="n"&gt;MyDb&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;FILEGROUP&lt;/span&gt; &lt;span class="n"&gt;MyDb_mod&lt;/span&gt; &lt;span class="k"&gt;CONTAINS&lt;/span&gt; &lt;span class="n"&gt;MEMORY_OPTIMIZED_DATA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;MyDb&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;FILE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MyDb_mod1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;ata&lt;/span&gt;&lt;span class="se"&gt;\M&lt;/span&gt;&lt;span class="s1"&gt;yDb_mod1'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;FILEGROUP&lt;/span&gt; &lt;span class="n"&gt;MyDb_mod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the real speedup comes from is pairing this with a natively compiled stored procedure. Interpreted T-SQL against a memory-optimized table is already faster than against a disk-based table, but it's still going through the interpreter. A natively compiled proc gets compiled to machine code at creation time:&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;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usp_AssignCart&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;SessionId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NATIVE_COMPILATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SCHEMABINDING&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="k"&gt;ATOMIC&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TRANSACTION&lt;/span&gt; &lt;span class="k"&gt;ISOLATION&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SNAPSHOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'us_english'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ShoppingCartId&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ShoppingCartId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ShoppingCartId&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserSession&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;SessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;SessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
        &lt;span class="n"&gt;THROW&lt;/span&gt; &lt;span class="mi"&gt;51000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'Session or cart not found.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserSession&lt;/span&gt;
    &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;ShoppingCartId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ShoppingCartId&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;SessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;SessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;BEGIN ATOMIC&lt;/code&gt; block and the mandatory isolation level. That's not optional boilerplate, it's how the engine decides transaction semantics for native code, and errors like arithmetic overflow or divide-by-zero get caught at creation time instead of runtime, which trips people up the first time a &lt;code&gt;CREATE PROCEDURE&lt;/code&gt; fails on a syntax-looking issue that's actually a data-type problem.&lt;/p&gt;

&lt;p&gt;On indexes: use a hash index only when every query against it is an equality predicate hitting every key column, point lookups by session ID or order ID. Use a nonclustered (range) index for anything with &lt;code&gt;ORDER BY&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;, or inequality comparisons. Getting this backwards is one of the more common mistakes, people default to hash indexes because they sound faster, then wonder why a range scan on that column is doing a full index scan instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this requires care
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Edition and version gating matters.&lt;/strong&gt; In-Memory OLTP has been available in every edition since SQL Server 2016 SP1, but Standard Edition caps you at 32 GB of memory-optimized data per database, and Express caps at 352 MB. If you're still on 2014 or 2016 RTM, it's Enterprise/Developer/Evaluation only. Check your edition before you architect around this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bucket count is a one-way decision, mostly.&lt;/strong&gt; Hash indexes need a bucket count set at creation, and getting it too low creates chain collisions, too high wastes memory permanently since you can't easily shrink it without rebuilding. As of more recent SQL Server versions you can &lt;code&gt;ALTER TABLE ... ALTER INDEX ... REBUILD WITH (BUCKET_COUNT = n)&lt;/code&gt;, but plan for this rather than guessing and hoping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;T-SQL support inside natively compiled procs is a real subset.&lt;/strong&gt; No &lt;code&gt;MERGE&lt;/code&gt;, limited join types in older versions, no cursors in the way you're used to. You will hit a wall porting an existing complex proc over verbatim. Budget time to rewrite, not just recompile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Foreign keys and CHECK constraints referencing a disk-based table from a memory-optimized one aren't supported&lt;/strong&gt;, and vice versa in some directions depending on version. If your memory-optimized table needs referential integrity against a regular table, you're enforcing it in application code or triggers, not the schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimistic concurrency assumes low conflict rates.&lt;/strong&gt; If two sessions are frequently trying to update the same row, you get conflict errors back to the app instead of blocking, and now your app needs retry logic it probably doesn't have. This is the one that bites people hardest: they migrate a hot table expecting free concurrency and instead get a wave of 41302/41305 errors under contention they didn't have with lock-based blocking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always Encrypted with secure enclaves doesn't work on memory-optimized columns&lt;/strong&gt;, and CLR code can't touch memory-optimized tables on the same connection. Check both before you commit to a migration if either is in your stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  My take
&lt;/h2&gt;

&lt;p&gt;In-Memory OLTP is a scalpel, not a general performance upgrade. If your profiler shows PAGELATCH or LCK_M waits stacking up on a specific hot table under real concurrency, and the table's working set actually fits in memory, this is one of the few SQL Server features that delivers the order-of-magnitude gains the marketing promises. If your problem is a missing index, a bad execution plan, or tempdb spills, move memory-optimized tables to the bottom of the list, they add operational complexity (memory sizing, checkpoint files, a genuinely different T-SQL subset) that isn't worth paying for a problem they don't solve. I've only reached for it maybe three times in production over the years, and every time it was because the wait stats told me to, not because a table was "slow."&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/introduction-to-memory-optimized-tables?view=sql-server-ver17" rel="noopener noreferrer"&gt;Introduction to Memory-Optimized Tables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/creating-a-memory-optimized-table-and-a-natively-compiled-stored-procedure?view=sql-server-ver17" rel="noopener noreferrer"&gt;Creating a Memory-Optimized Table and a Natively Compiled Stored Procedure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/requirements-for-using-memory-optimized-tables?view=sql-server-ver17" rel="noopener noreferrer"&gt;Requirements for Using Memory-Optimized Tables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/indexes-for-memory-optimized-tables?view=sql-server-ver17" rel="noopener noreferrer"&gt;Indexes for Memory-Optimized Tables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sqlserver</category>
      <category>database</category>
      <category>performance</category>
      <category>tsql</category>
    </item>
    <item>
      <title>Oracle GoldenGate 23ai: Real-Time Replication to the Cloud Without a Downtime Window</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Thu, 15 Oct 2026 13:52:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/oracle-goldengate-23ai-real-time-replication-to-the-cloud-without-a-downtime-window-44e8</link>
      <guid>https://dev.to/arvind_toorpu/oracle-goldengate-23ai-real-time-replication-to-the-cloud-without-a-downtime-window-44e8</guid>
      <description>&lt;p&gt;Last quarter I migrated a 4TB on-prem Oracle order-management database to OCI Autonomous Database with a change window of exactly zero minutes. No maintenance page, no "please don't place orders between 2 and 4 AM." The tool that made that possible was Oracle GoldenGate 23ai, and if you're still running the classic architecture from a few years ago, the upgrade path itself is worth knowing about before you touch a single extract.&lt;/p&gt;

&lt;p&gt;Here's what actually changed, why it matters for cloud migrations specifically, and where I got bitten during setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What GoldenGate 23ai actually is
&lt;/h2&gt;

&lt;p&gt;GoldenGate has always done one thing: capture committed changes from a source database's redo/transaction logs and apply them to a target, in near real time, without touching the source workload. 23ai (GA since April 2024, still shipping incremental releases under that name) adds a few things that matter if your target is a cloud database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Microservices architecture is now the only architecture.&lt;/strong&gt; Classic GGSCI-only deployments are on their way out. Everything runs as a set of REST-managed services (Administration Server, Distribution Server, Receiver Server) fronted by a browser console, managed through &lt;code&gt;adminclient&lt;/code&gt; instead of the old &lt;code&gt;ggsci&lt;/code&gt; shell alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI vector data type support.&lt;/strong&gt; GoldenGate can now replicate Oracle 23ai &lt;code&gt;VECTOR&lt;/code&gt; columns in real time, including to non-Oracle vector stores like pgvector, MySQL HeatWave, and OpenSearch, as long as the embedding algorithm and dimensions match on both sides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Streams.&lt;/strong&gt; DDL and DML changes can be exposed as JSON CloudEvents over AsyncAPI, so a downstream microservice can subscribe to a table's changes without you writing a Kafka Connect pipeline for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCI GoldenGate as a managed service.&lt;/strong&gt; Fully managed deployments across 45+ OCI regions with elastic autoscaling, so you're not babysitting a VM running the hub.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a straight database migration, the piece that matters most is still the boring one: extract, trail, replicat. Let's build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up real-time replication to the cloud
&lt;/h2&gt;

&lt;p&gt;Assume an on-prem Oracle 19c/23ai source and an OCI Autonomous Database (or Oracle on RDS, the pattern is nearly identical) as target. First, enable supplemental logging on the source. Do this at the PDB level, not the CDB root, unless you're specifically doing downstream mining, Oracle explicitly does not recommend root-level capture with a common user for normal replication.&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;-- Run inside the source PDB, connected as the GoldenGate admin user&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SYSTEM&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;ENABLE_GOLDENGATE_REPLICATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;TRUE&lt;/span&gt; &lt;span class="k"&gt;SCOPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;BOTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_HEADER&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;SUPPLEMENTAL&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;COLUMNS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, from the admin client (or GGSCI if you're still mid-upgrade), enable trandata and add the extract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GGSCI&amp;gt; DBLOGIN USERID ggadmin@ORCLPDB1, PASSWORD &lt;span class="k"&gt;******&lt;/span&gt;
GGSCI&amp;gt; ADD TRANDATA ORDERS.ORDER_HEADER
GGSCI&amp;gt; ADD EXTRACT EXTORD, TRANLOG, INTEGRATED TRANLOG, BEGIN NOW
GGSCI&amp;gt; ADD EXTTRAIL /u01/ogg/dirdat/ab, EXTRACT EXTORD, MEGABYTES 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extract's parameter file is where you actually tell it what to capture:&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;EXTRACT&lt;/span&gt; &lt;span class="n"&gt;EXTORD&lt;/span&gt;
&lt;span class="n"&gt;USERID&lt;/span&gt; &lt;span class="n"&gt;ggadmin&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ORCLPDB1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;******&lt;/span&gt;
&lt;span class="n"&gt;EXTTRAIL&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;u01&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ogg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dirdat&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;
&lt;span class="n"&gt;TRANLOGOPTIONS&lt;/span&gt; &lt;span class="n"&gt;EXCLUDEUSER&lt;/span&gt; &lt;span class="n"&gt;GGADMIN&lt;/span&gt;
&lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_HEADER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_LINES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the target side (Autonomous Database, connecting over the wallet TNS alias), add the replicat against a checkpoint table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GGSCI&amp;gt; DBLOGIN USERID ggadmin@ORCLADB_HIGH, PASSWORD &lt;span class="k"&gt;******&lt;/span&gt;
GGSCI&amp;gt; ADD CHECKPOINTTABLE ggadmin.ggchkpt
GGSCI&amp;gt; ADD REPLICAT REPORD, EXTTRAIL /u01/ogg/dirdat/ab, CHECKPOINTTABLE ggadmin.ggchkpt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And its parameter file:&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="n"&gt;REPLICAT&lt;/span&gt; &lt;span class="n"&gt;REPORD&lt;/span&gt;
&lt;span class="n"&gt;USERID&lt;/span&gt; &lt;span class="n"&gt;ggadmin&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ORCLADB_HIGH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;******&lt;/span&gt;
&lt;span class="n"&gt;ASSUMETARGETDEFS&lt;/span&gt;
&lt;span class="k"&gt;MAP&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_HEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_HEADER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;MAP&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_LINES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="n"&gt;ORDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ORDER_LINES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start both, watch the lag, and once &lt;code&gt;REPORD&lt;/code&gt; reports zero lag against a quiet source, you cut over the application's connection string. That's the whole trick to a zero-downtime migration: the cutover is a DNS/connection-string change, not a data-loading exercise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GGSCI&amp;gt; START EXTRACT EXTORD
GGSCI&amp;gt; START REPLICAT REPORD
GGSCI&amp;gt; INFO REPLICAT REPORD, DETAIL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where this requires care
&lt;/h2&gt;

&lt;p&gt;A few things cost me real time on that migration, and I'd rather you skip them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The classic-to-microservices jump isn't optional anymore.&lt;/strong&gt; If you're on an older GoldenGate install, plan a separate upgrade step before the migration project, not during it. Trying to learn &lt;code&gt;adminclient&lt;/code&gt; REST semantics on the same day you're cutting a production database over is a bad combination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autonomous Database needs the wallet, and the wallet needs network ACLs.&lt;/strong&gt; GoldenGate connects to Autonomous Database over TLS using the downloaded wallet's TNS entry. If your GoldenGate host can't reach the ADB's public or private endpoint, or the ACL on the ADB instance hasn't been opened for that host, you'll get a connection failure that looks like a credentials problem and isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trail files will eat your disk if replicat lags.&lt;/strong&gt; On a large initial sync, extract runs ahead of replicat easily. Watch trail directory growth, not just lag in seconds, especially if the target is on a slower network path like a cross-region OCI link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vector column replication has real constraints.&lt;/strong&gt; It's not a magic heterogeneous conversion. Source and target vector dimensions and distance metric need to line up, or you're replicating garbage embeddings that happen to be the right data type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root-level capture with a common user works but isn't the recommended path.&lt;/strong&gt; If you inherited a GoldenGate config that captures from the CDB root, budget time to migrate it to per-PDB extracts before you lean on it for a cloud migration you actually care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  My take
&lt;/h2&gt;

&lt;p&gt;GoldenGate 23ai's headline features (vector replication, Data Streams, the AI service integrations) are the interesting demo material, but the reason it's worth learning right now is much less flashy: it's the most reliable way I've found to move a live Oracle database to the cloud without asking the business for a maintenance window. The microservices architecture switch is a real cost if you're upgrading from an old classic install, but once it's running, the extract-trail-replicat pattern is the same discipline it's always been, just pointed at a cloud endpoint instead of a second on-prem box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/middleware/goldengate/core/23/coredoc/why-upgrade-ogg23.html" rel="noopener noreferrer"&gt;Oracle GoldenGate 23ai: What's New&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blogs.oracle.com/dataintegration/announcing-goldengate-23ai" rel="noopener noreferrer"&gt;Announcing GoldenGate 23ai General Availability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/goldengate/doc/add-replicat-odb.html" rel="noopener noreferrer"&gt;Add a Replicat for Autonomous AI Database (OCI GoldenGate)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.OracleGoldenGate.ExtractReplicat.html" rel="noopener noreferrer"&gt;Working with the EXTRACT and REPLICAT utilities of Oracle GoldenGate (AWS RDS docs)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>oracle</category>
      <category>database</category>
      <category>replication</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Amazon RDS Custom for Oracle: Root Access to Your DB Host Without Giving Up Managed Backups</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Mon, 05 Oct 2026 13:51:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/amazon-rds-custom-for-oracle-root-access-to-your-db-host-without-giving-up-managed-backups-3efh</link>
      <guid>https://dev.to/arvind_toorpu/amazon-rds-custom-for-oracle-root-access-to-your-db-host-without-giving-up-managed-backups-3efh</guid>
      <description>&lt;p&gt;A few years back I had a client running Oracle EE on regular RDS who needed a third-party monitoring agent installed at the OS level. Regular RDS for Oracle said no, flatly, there is no SSH, no root, no filesystem access, period. The workaround people usually reach for is "just run it on EC2 yourself" and eat the patching and backup burden forever. That tradeoff is exactly what RDS Custom for Oracle exists to remove, and if you have not looked at it since it launched, the details of how you actually get that access (and what it costs you operationally) are worth understanding before you commit a production workload to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What RDS Custom for Oracle actually is
&lt;/h2&gt;

&lt;p&gt;It is not a third option between RDS and EC2, it is both at once. AWS still provisions and manages the underlying EC2 instance, still handles automated backups, patching orchestration, and monitoring, but it gives you SSH and root access to that instance and the ability to install agents, apply one-off patches, or tweak OS parameters that regular RDS locks away. The mechanism that makes this safe(ish) is called automation mode. When automation is in "full" mode, RDS Custom actively manages the instance and will overwrite or flag anything you change by hand. You pause it before you touch anything, do your work, then resume it.&lt;/p&gt;

&lt;p&gt;The other piece that is different from stock RDS is the custom engine version (CEV). Instead of picking from AWS's list of Oracle versions, you build your own CEV from your own Oracle installation media and patch bundles, stored in S3. That is also how you get to bring specific PSU/RU levels or one-off patches that AWS has not pushed to the standard RDS for Oracle fleet yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a CEV and creating the instance
&lt;/h2&gt;

&lt;p&gt;You need Oracle installation files and patches uploaded to an S3 bucket in the same region as the CEV, plus a manifest describing them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mediaImportTemplateVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-08-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"databaseInstallationFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"V982063-01.zip"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"opatchFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"p6880880_190000_Linux-x86-64.zip"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"psuRuPatchFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"p32126828_190000_Linux-x86-64.zip"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"installationParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"unixGroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"unixUname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oracle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"oracleHome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/oracle/oracle.19.0.0.0.ru-2020-04.rur-2020-04.r1.EE.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"oracleBase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/oracle/"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the CEV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds create-custom-db-engine-version &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--engine&lt;/span&gt; custom-oracle-ee-cdb &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--engine-version&lt;/span&gt; 19.cdb_cev1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--database-installation-files-s3-bucket-name&lt;/span&gt; us-east-1-123456789012-custom-installation-files &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--database-installation-files-s3-prefix&lt;/span&gt; 123456789012/cev1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--kms-key-id&lt;/span&gt; arn:aws:kms:us-east-1:123456789012:key/abcd1234-ef56-7890-ab12-cd34ef567890 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--manifest&lt;/span&gt; file://manifest.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Budget roughly two hours for this to finish, and know going in that a CEV's architecture is permanent once created. Get the manifest wrong and you delete and start over, you cannot patch it in place.&lt;/p&gt;

&lt;p&gt;Once the CEV shows &lt;code&gt;available&lt;/code&gt;, spin up the instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds create-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--engine&lt;/span&gt; custom-oracle-ee-cdb &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-cfo-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--engine-version&lt;/span&gt; 19.cdb_cev1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-name&lt;/span&gt; MYPDB &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-system-id&lt;/span&gt; MYCDB &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--allocated-storage&lt;/span&gt; 250 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-class&lt;/span&gt; db.m5.xlarge &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-subnet-group&lt;/span&gt; mydbsubnetgroup &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--master-username&lt;/span&gt; myuser &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--master-user-password&lt;/span&gt; &lt;span class="s1"&gt;'change-me-now'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--backup-retention-period&lt;/span&gt; 7 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--kms-key-id&lt;/span&gt; my-kms-key &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--no-auto-minor-version-upgrade&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--custom-iam-instance-profile&lt;/span&gt; AWSRDSCustomInstanceProfile-us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IAM instance profile has to start with &lt;code&gt;AWSRDSCustom&lt;/code&gt;, this is how RDS Custom's control plane distinguishes an instance it is allowed to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the SSH access you actually asked for
&lt;/h2&gt;

&lt;p&gt;RDS Custom drops an SSH private key into Secrets Manager, named something like &lt;code&gt;do-not-delete-rds-custom-ssh-privatekey-&amp;lt;resource-id&amp;gt;-&amp;lt;uuid&amp;gt;&lt;/code&gt;. Pull it down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws secretsmanager get-secret-value &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--secret-id&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-not-delete-rds-custom-ssh-privatekey-db-ABCDEFGHIJKLMNOPQRS0123456-0d726c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--query&lt;/span&gt; SecretString &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cfo-instance.pem

&lt;span class="nb"&gt;chmod &lt;/span&gt;400 cfo-instance.pem
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; cfo-instance.pem ec2-user@ec2-12-345-678-901.us-east-2.compute.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a private instance without a public IP, tunnel through Systems Manager instead of exposing port 22:&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;# add to ~/.ssh/config&lt;/span&gt;
&lt;span class="c"&gt;# Host i-* mi-*&lt;/span&gt;
&lt;span class="c"&gt;#     ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"&lt;/span&gt;

ssh &lt;span class="nt"&gt;-i&lt;/span&gt; cfo-instance.pem ec2-user@i-0bdc4219e66944afa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you are in as &lt;code&gt;ec2-user&lt;/code&gt;, &lt;code&gt;sudo su - rdsdb&lt;/code&gt; gets you the database owner account, and &lt;code&gt;sudo su -&lt;/code&gt; gets you root. Before you do anything, pause automation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds modify-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; my-cfo-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--automation-mode&lt;/span&gt; all-paused &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--resume-full-automation-mode-minutes&lt;/span&gt; 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last flag is your safety net, automation resumes on its own after 60 minutes even if you forget to flip it back manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this requires care
&lt;/h2&gt;

&lt;p&gt;The end-of-support date matters more than most gotchas here. AWS has set March 31, 2027 as the end of support for RDS Custom for Oracle, with the migration window (to self-managed Oracle on EC2, via RMAN active duplication or Data Guard) running now through then. If you are standing up a new RDS Custom for Oracle workload today, plan the exit before you plan the entry.&lt;/p&gt;

&lt;p&gt;A few operational ones too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard Edition 2 on RDS Custom caps you at 16 vCPUs and 3 tenant databases per CDB, and Data Guard (so read replicas) is off the table on SE2 entirely.&lt;/li&gt;
&lt;li&gt;Leaving automation paused longer than necessary means no automated backups, no patching, and no health monitoring during that window. I have seen a "quick 20 minute fix" turn into a half-day troubleshooting session with automation sitting paused the whole time, that is an outage waiting to happen if something else goes wrong concurrently.&lt;/li&gt;
&lt;li&gt;A CEV manifest mistake is not a warning, it is a failed CEV you delete and rebuild, no patching in place.&lt;/li&gt;
&lt;li&gt;You cannot reset the master password through the console's Modify action the way you can on regular RDS, it has to be an &lt;code&gt;ALTER USER&lt;/code&gt; from inside the database.&lt;/li&gt;
&lt;li&gt;Resource IDs, key names, and SSH secret names are all auto-generated and ugly. Script the lookups (&lt;code&gt;describe-db-instances&lt;/code&gt;, &lt;code&gt;describe-instances&lt;/code&gt;, &lt;code&gt;get-secret-value&lt;/code&gt;) rather than hunting through the console every time you need to connect.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My take
&lt;/h2&gt;

&lt;p&gt;RDS Custom for Oracle is the right answer for a narrow but real problem: you need root or SSH for something specific (an agent, a kernel parameter, a one-off patch) and you are not willing to give up managed backups and patch orchestration to get it. It is not a general-purpose upgrade from regular RDS for Oracle, and given the 2027 end-of-support date, I would not recommend it as the long-term home for a new mission-critical system without a migration plan already sketched out. Use it as a bridge, not a destination, and pause automation like you mean it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/custom-cev.create.html" rel="noopener noreferrer"&gt;Creating a CEV, Amazon RDS User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/custom-oracle-feature-support.html" rel="noopener noreferrer"&gt;Feature availability and support for RDS Custom for Oracle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/RDS-Custom-for-Oracle-end-of-support.html" rel="noopener noreferrer"&gt;RDS Custom for Oracle end of support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/database/part-1-make-configuration-changes-to-an-amazon-rds-custom-for-oracle-instance/" rel="noopener noreferrer"&gt;Make configuration changes to an Amazon RDS Custom for Oracle instance: Part 1, AWS Database Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>oracle</category>
      <category>rds</category>
      <category>database</category>
    </item>
    <item>
      <title>Unlocking the Power of JSON Schema with Oracle Database 23ai</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Mon, 21 Sep 2026 14:48:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/unlocking-the-power-of-json-schema-with-oracle-database-23ai-35p</link>
      <guid>https://dev.to/arvind_toorpu/unlocking-the-power-of-json-schema-with-oracle-database-23ai-35p</guid>
      <description>&lt;h1&gt;
  
  
  Unlocking the Power of JSON Schema with Oracle Database 23ai
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Staying current with the latest innovations in &lt;strong&gt;Oracle 23AI&lt;/strong&gt; is critical for organizations seeking high performance, reliability, and automation from their &lt;strong&gt;cloud database&lt;/strong&gt; solutions. AWS is rapidly evolving its managed database offerings, and Oracle Database 23ai introduces game-changing features that can transform how enterprises store, validate, and manage JSON data. One particularly exciting innovation is the introduction of &lt;strong&gt;JSON Schema&lt;/strong&gt; support—a leap forward for developers building modern, data-driven applications on &lt;strong&gt;Oracle 23AI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is JSON Schema in Oracle Database 23ai AWS RDS?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JSON Schema&lt;/strong&gt; provides a robust, standardized way to define, document, and validate the structure and constraints of JSON data in your Oracle databases. With Oracle Database 23ai now available , organizations can directly leverage JSON Schema validation to ensure their semi-structured data complies with expected formats—boosting data integrity and development agility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud database automation&lt;/strong&gt;: Automate data validation as JSON is ingested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stronger data consistency&lt;/strong&gt;: Enforce standards for documents at the database layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced application logic&lt;/strong&gt;: Offload validation to the database, simplifying codebases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced developer agility&lt;/strong&gt;: Accelerate API and microservices development with reliable data models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved compliance&lt;/strong&gt;: Simplify auditing and data governance for regulatory standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How JSON Schema Works in Oracle Database 23ai RDS
&lt;/h2&gt;

&lt;p&gt;Oracle Database 23ai natively integrates the &lt;strong&gt;JSON Schema&lt;/strong&gt; standard (draft 2020-12), allowing you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register JSON Schema definitions in the database&lt;/li&gt;
&lt;li&gt;Associate schemas with specific JSON columns or tables&lt;/li&gt;
&lt;li&gt;Automatically validate incoming JSON objects against defined schemas&lt;/li&gt;
&lt;li&gt;Get detailed error reports for validation failures&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Oracle Database 23ai&lt;/strong&gt; instance running on &lt;strong&gt;AWS RDS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;RDS role privileges to create and manage JSON schemas&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;JSON_SCHEMA&lt;/code&gt; package enabled in your schema&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enabling and Using JSON Schema in AWS RDS Oracle 23ai
&lt;/h2&gt;

&lt;p&gt;Follow these steps to get started:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Define Your JSON Schema
&lt;/h3&gt;

&lt;p&gt;Save your schema as a string or in a file. For example, a simple “person” schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Register the JSON Schema in Oracle
&lt;/h3&gt;

&lt;p&gt;Use the new PL/SQL package &lt;code&gt;DBMS_JSON_SCHEMA&lt;/code&gt; to register your 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;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_JSON_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REGISTER_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;schema_url&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://mycorp.com/schemas/person'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;schema_doc&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{ 
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "title": "Person",
      "type": "object",
      "properties": {
        "fullName": { "type": "string" },
        "age": { "type": "integer", "minimum": 0 }
      },
      "required": ["fullName", "age"]
    }'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Associate JSON Schema with a Table Column
&lt;/h3&gt;

&lt;p&gt;Suppose you have a table &lt;code&gt;users&lt;/code&gt; with a JSON column &lt;code&gt;profile&lt;/code&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="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;JSON_SCHEMA_VALID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s1"&gt;'https://mycorp.com/schemas/person'&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;h3&gt;
  
  
  4. Attempt to Insert Invalid Data
&lt;/h3&gt;

&lt;p&gt;If an insert violates the schema, Oracle will reject it:&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;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="s1"&gt;'{"fullName": "Dana White", "age": -5}'&lt;/span&gt;  &lt;span class="c1"&gt;-- Invalid: age &amp;lt; 0&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- ERROR: fails validation due to schema violation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Use Case: Automated Data Quality for Customer Onboarding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario
&lt;/h3&gt;

&lt;p&gt;A financial services SaaS provider leverages &lt;strong&gt;multi-AZ Oracle RDS&lt;/strong&gt; to power its customer onboarding application. Their microservices architecture sends JSON payloads with user profiles directly to a central “users” table in Oracle.&lt;/p&gt;

&lt;h3&gt;
  
  
  The JSON Schema Advantage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;By registering a JSON Schema for user profiles, every inbound data record is automatically validated.&lt;/li&gt;
&lt;li&gt;No more custom app-layer validation code; data stewards simply update the schema as policy evolves.&lt;/li&gt;
&lt;li&gt;Compliance teams gain a clear audit trail for every rejected or malformed payload.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster application rollouts&lt;/strong&gt; due to simplified API validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stronger compliance posture&lt;/strong&gt; with built-in, granular data enforcement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced outages&lt;/strong&gt; caused by malformed or incomplete data.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The support for JSON Schema in Oracle Database 23ai gives organizations unprecedented control over their semi-structured data. By elevating data validation to the database layer, teams can automate quality, improve governance, and accelerate development cycles. As data ecosystems become more complex, expect features like these to shape the future of cloud database automation and drive even greater enterprise agility.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>json</category>
      <category>oracle</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Your AG Secondaries Aren't Load Balancing Because You Skipped One Step</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Mon, 14 Sep 2026 13:46:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/your-ag-secondaries-arent-load-balancing-because-you-skipped-one-step-3bda</link>
      <guid>https://dev.to/arvind_toorpu/your-ag-secondaries-arent-load-balancing-because-you-skipped-one-step-3bda</guid>
      <description>&lt;p&gt;A client paged me at 6am because their reporting dashboards were timing out. Nothing was down. The primary replica in their Always On AG was just getting hammered by every single read query in the environment, while two perfectly healthy secondary replicas sat there doing nothing but redo. They had "configured" read-scale months earlier: &lt;code&gt;ALLOW_CONNECTIONS = READ_ONLY&lt;/code&gt; was set on both secondaries, the app connection string had &lt;code&gt;ApplicationIntent=ReadOnly&lt;/code&gt;, and none of it mattered because nobody had built the read-only routing list on the primary. Connections with read intent were falling through to the primary because that's what happens by default when a routing list doesn't exist.&lt;/p&gt;

&lt;p&gt;That's the whole point of this post. Making a secondary readable and getting traffic to actually route to it are two different configuration steps, and skipping the second one is the single most common mistake I see in AG read-scale setups.&lt;/p&gt;

&lt;h2&gt;
  
  
  What read-only routing actually is
&lt;/h2&gt;

&lt;p&gt;Read-only routing is a primary-replica-side redirect. A client connects to the listener (or, in a clusterless read-scale AG, to a replica directly) with &lt;code&gt;ApplicationIntent=ReadOnly&lt;/code&gt; in the connection string. If the instance it lands on is the primary, SQL Server checks whether a read-only routing list exists for that primary. If one does, it transparently redirects the connection to whichever secondary the list points to. No list, no redirect, the connection just runs read-write on the primary like normal.&lt;/p&gt;

&lt;p&gt;Since SQL Server 2017 you don't even need a Windows Server Failover Cluster to get this: a read-scale availability group can exist without a cluster at all, purely to fan out read workload across replicas (&lt;a href="https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/read-scale-availability-groups?view=sql-server-ver17" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;). Be clear with yourself and your team about what that buys you, though: a cluster-less read-scale AG has no automatic failover. There's no cluster manager watching health and flipping the primary role for you. If you need that, you're building a normal Always On AG with WSFC (or Pacemaker on Linux) and layering read-only routing on top of it, which is the far more common production pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring it end to end
&lt;/h2&gt;

&lt;p&gt;Here's the sequence that actually works, on a standard Windows-clustered AG with two readable secondaries.&lt;/p&gt;

&lt;p&gt;First, mark each secondary as readable and give it a routing URL:&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;AVAILABILITY&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AG1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;REPLICA&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE02'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECONDARY_ROLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ALLOW_CONNECTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;READ_ONLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;READ_ONLY_ROUTING_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'TCP://SQLNODE02.corp.local:1433'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;AVAILABILITY&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AG1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;REPLICA&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE03'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECONDARY_ROLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ALLOW_CONNECTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;READ_ONLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;READ_ONLY_ROUTING_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'TCP://SQLNODE03.corp.local:1433'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the step people actually remember. Here's the one they don't: the routing list has to be defined on whichever replica is currently the primary, because that's the instance doing the redirecting.&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;AVAILABILITY&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AG1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;REPLICA&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE01'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRIMARY_ROLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;READ_ONLY_ROUTING_LIST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE02'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE03'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'SQLNODE01'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That nested-parentheses syntax, &lt;code&gt;(('SQLNODE02','SQLNODE03'), 'SQLNODE01')&lt;/code&gt;, is load-balanced round-robin routing across SQLNODE02 and SQLNODE03, with SQLNODE01 (the primary) as the fallback if both are unavailable. That's a SQL Server 2016+ feature; only one level of nesting is supported (&lt;a href="https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/configure-read-only-routing-for-an-availability-group-sql-server?view=sql-server-ver17" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Because any node can become primary during a failover, you need to repeat that &lt;code&gt;PRIMARY_ROLE&lt;/code&gt; routing list definition on every replica, each one listing itself last as the fallback:&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;AVAILABILITY&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AG1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;REPLICA&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE02'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRIMARY_ROLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;READ_ONLY_ROUTING_LIST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE03'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'SQLNODE02'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;AVAILABILITY&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AG1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;REPLICA&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE03'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRIMARY_ROLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;READ_ONLY_ROUTING_LIST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'SQLNODE02'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'SQLNODE03'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the client side, the connection string needs the intent flag and, if the listener spans subnets, multi-subnet failover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;tcp:AG1-Listener,1433;Database=Sales;Application Intent=ReadOnly;MultiSubnetFailover=True;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For ad hoc testing from SSMS or &lt;code&gt;sqlcmd&lt;/code&gt;, you can confirm which node you actually landed on:&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="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;SERVERNAME&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;routed_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn_hadr_is_primary_replica&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sales'&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;is_primary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to check what's actually registered on the primary before you blame the app:&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;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ag_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replica_server_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_only_routing_url&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;availability_read_only_routing_lists&lt;/span&gt; &lt;span class="n"&gt;rl&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;availability_replicas&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replica_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replica_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;availability_groups&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That query would have caught my client's problem in about ten seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this requires care
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The primary needs a routing URL too.&lt;/strong&gt; If the routing list ever falls back to the primary (or a client connects to the primary directly with read intent and no list exists yet), and the primary itself has no &lt;code&gt;READ_ONLY_ROUTING_URL&lt;/code&gt;, routing fails outright rather than silently running on the primary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load-balanced routing is round-robin per new connection, not per query.&lt;/strong&gt; It's not smart about replica lag or current load. If SQLNODE03 is 40 seconds behind on redo because of a big index rebuild, round-robin will happily send a client there and that client will see stale data with no warning. If your reporting workload is lag-sensitive, monitor &lt;code&gt;sys.dm_hadr_database_replica_states.secondary_lag_seconds&lt;/code&gt; and either alert on it or take that replica out of the routing list temporarily during heavy write windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Readable secondaries are not free connections.&lt;/strong&gt; They're separate SQL Server instances and, outside a passive failover-only configuration, they need to be licensed like any other active SQL Server. This trips up teams who treat "we already pay for AG" as covering read-scale for free. Check your licensing terms, don't assume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Availability Groups on Standard Edition don't support readable secondaries at all.&lt;/strong&gt; If you're on Standard Edition, you get one AG, one database, no read access to the secondary, full stop. Read-scale in the way this article describes needs Enterprise Edition (or SQL Server 2017+ read-scale-without-a-cluster, which has its own edition rules, worth double-checking against your specific version).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Intent is case-sensitive in spelling but the driver matters more than you'd think.&lt;/strong&gt; Some older ODBC/JDBC driver versions silently drop or mishandle &lt;code&gt;ApplicationIntent&lt;/code&gt;. If routing isn't happening and your T-SQL config checks out clean, check the actual driver version and connection string the app is sending, not just what you think you configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  My take
&lt;/h2&gt;

&lt;p&gt;Read-only routing is one of those Always On features that looks like a checkbox in the wizard and is actually two independent, un-obviously-linked configuration objects: the secondary's readability plus URL, and the primary's routing list. SSMS's Add Replica wizard doesn't force you through the second one clearly, and I've seen more environments get half-configured than fully configured. If you're rolling this out, script both steps together, verify with the DMV query above as part of your deployment, and don't trust the dashboard alone to tell you traffic is actually landing where you think it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/configure-read-only-routing-for-an-availability-group-sql-server?view=sql-server-ver17" rel="noopener noreferrer"&gt;Configure read-only routing for an availability group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/read-scale-availability-groups?view=sql-server-ver17" rel="noopener noreferrer"&gt;Use read-scale with Always On availability groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-availability-read-only-routing-lists-transact-sql?view=sql-server-ver17" rel="noopener noreferrer"&gt;sys.availability_read_only_routing_lists (Transact-SQL)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-availability-group-configure-rs?view=sql-server-ver16" rel="noopener noreferrer"&gt;Configure a Read-Scale Availability Group (SQL Server on Linux)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sqlserver</category>
      <category>database</category>
      <category>ha</category>
      <category>tsql</category>
    </item>
    <item>
      <title>S3 Express One Zone Got Cheaper and Gained Inventory Support. Time to Reconsider It</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Thu, 10 Sep 2026 15:52:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/s3-express-one-zone-got-cheaper-and-gained-inventory-support-time-to-reconsider-it-3pi5</link>
      <guid>https://dev.to/arvind_toorpu/s3-express-one-zone-got-cheaper-and-gained-inventory-support-time-to-reconsider-it-3pi5</guid>
      <description>&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%2F38kzgchjvisrb31i9k3n.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%2F38kzgchjvisrb31i9k3n.png" alt="S3 Express One Zone Got Cheaper and Gained Inventory Support" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;S3 Express One Zone launched back in 2023 with a pretty specific pitch: single-digit millisecond latency, up to 10x faster than S3 Standard, for workloads that actually need it. The catch was cost and a missing feature set that made it hard to run in production the same way you'd run Standard. Two 2026 updates change that calculus: a big price cut, and S3 Inventory support finally landing in April.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pitch, quickly
&lt;/h2&gt;

&lt;p&gt;S3 Express One Zone stores data in a single Availability Zone (hence "One Zone") in a directory bucket, not the usual S3 bucket structure. In exchange for giving up multi-AZ redundancy, you get consistent low-latency access, request costs up to 80% lower than Standard, and throughput that's genuinely built for latency-sensitive access patterns: ML training data loading, interactive analytics, key-value caching for AI search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api create-bucket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-express-bucket--use1-az4--x-s3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-bucket-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{"Location":{"Type":"AvailabilityZone","Name":"use1-az4"},"Bucket":{"Type":"Directory","DataRedundancy":"SingleAvailabilityZone"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the bucket naming convention: directory buckets need that &lt;code&gt;--use1-az4--x-s3&lt;/code&gt; suffix pattern, they're not just a regular bucket with a flag flipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The price cut
&lt;/h2&gt;

&lt;p&gt;AWS announced price reductions of up to 85% for S3 Express One Zone. That's a meaningful shift in the cost-benefit calculation. At the original pricing, the latency win had to be worth a real premium to justify using it over Standard plus a caching layer. At the new pricing, that math looks a lot more favorable for a wider range of workloads, not just the most latency-obsessed ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  S3 Inventory support (April 2026)
&lt;/h2&gt;

&lt;p&gt;This is the operational gap that made Express One Zone hard to run seriously before. S3 Inventory generates scheduled reports (daily or weekly) listing every object in a bucket or prefix along with metadata like size, encryption status, and last modified time. Without it, if you wanted to know what was actually sitting in a directory bucket, you were stuck listing objects programmatically, which doesn't scale well for buckets with millions of objects.&lt;/p&gt;

&lt;p&gt;Now you can set it up the same way you would for a Standard bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-bucket-inventory-configuration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-express-bucket--use1-az4--x-s3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--id&lt;/span&gt; daily-inventory &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--inventory-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Destination": {
      "S3BucketDestination": {
        "Bucket": "arn:aws:s3:::my-inventory-reports",
        "Format": "Parquet"
      }
    },
    "IsEnabled": true,
    "Id": "daily-inventory",
    "IncludedObjectVersions": "Current",
    "Schedule": {"Frequency": "Daily"}
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output landing as Parquet in a regular bucket means you can query it directly with Athena, which is a much saner way to audit a large directory bucket than paging through ListObjectsV2 results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it actually fits
&lt;/h2&gt;

&lt;p&gt;The realistic use cases haven't changed, they've just gotten cheaper to justify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ML training data staging.&lt;/strong&gt; Loading training batches with consistent low latency, especially paired with SageMaker's optimized data channels, which now integrate natively with directory buckets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive analytics scratch space.&lt;/strong&gt; Query engines that need to read and write intermediate results fast, where S3 Standard's latency profile becomes the bottleneck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI search key-value caching.&lt;/strong&gt; Storing embeddings or lookup data where request latency directly affects user-facing response time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where it doesn't fit
&lt;/h2&gt;

&lt;p&gt;Single-AZ means single-AZ. If the AZ your directory bucket lives in has an outage, your data is unavailable until that AZ recovers, full stop. This isn't a subtle tradeoff, it's the whole design point of the product, and it means Express One Zone is a poor fit for anything that's your durable source of truth. Treat it as a performance tier for data that either has a durable copy elsewhere (like the original training dataset in S3 Standard) or is inherently ephemeral (like intermediate compute results you can regenerate).&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it without commitment
&lt;/h2&gt;

&lt;p&gt;Availability has been expanding steadily, Frankfurt joined the list of supported Regions in July 2026, bringing the total to eight. If your workload's home Region wasn't covered before, it's worth checking again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api list-directory-buckets &lt;span class="nt"&gt;--region&lt;/span&gt; eu-central-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A quick cost sanity check before you migrate anything
&lt;/h2&gt;

&lt;p&gt;Before moving a workload over, it's worth running a small side-by-side test rather than trusting the headline "up to 85% cheaper" number blindly, since your actual savings depend heavily on your specific request pattern (lots of small GETs versus fewer large ones behaves very differently on request-based pricing). Stand up a directory bucket, mirror a day's worth of real traffic against it alongside your existing Standard bucket, and compare the actual bill delta rather than the marketing number. It's a couple hours of setup that turns "AWS says it's cheaper" into "here's what it actually costs for our workload."&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;If you evaluated S3 Express One Zone in 2023 or 2024 and decided the cost didn't justify the latency win for your workload, it's genuinely worth re-running that math now. Between the price cut and Inventory support closing the biggest operational gap, this moved from "niche latency tool" to "reasonable default for a wider set of performance-sensitive workloads" in about a year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/aws/up-to-85-price-reductions-for-amazon-s3-express-one-zone/" rel="noopener noreferrer"&gt;Announcing up to 85% price reductions for Amazon S3 Express One Zone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/04/s3-express-one-zone-supports-s3-inventory/" rel="noopener noreferrer"&gt;Amazon S3 Express One Zone now supports S3 Inventory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-bucket-high-performance.html" rel="noopener noreferrer"&gt;High performance workloads with S3 directory buckets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>s3</category>
      <category>cloud</category>
      <category>data</category>
    </item>
    <item>
      <title>SnapStart Basically Kills Lambda Cold Starts for Java, Python, and .NET. Here's How to Actually Use It</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Fri, 04 Sep 2026 12:49:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/snapstart-basically-kills-lambda-cold-starts-for-java-python-and-net-heres-how-to-actually-use-36g9</link>
      <guid>https://dev.to/arvind_toorpu/snapstart-basically-kills-lambda-cold-starts-for-java-python-and-net-heres-how-to-actually-use-36g9</guid>
      <description>&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%2Fdg7mj3vislz7dxcdk7oq.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%2Fdg7mj3vislz7dxcdk7oq.png" alt="SnapStart Basically Kills Lambda Cold Starts for Java" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cold starts are the one Lambda complaint that never really goes away, especially if you're running Java or a heavier Python stack. SnapStart is AWS's answer, and it's now generally available for Java, Python 3.12+, and .NET 8+. If you haven't touched it yet, this is worth an afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SnapStart actually does
&lt;/h2&gt;

&lt;p&gt;Instead of initializing your function's runtime, dependencies, and execution environment from a cold start every time, SnapStart takes a snapshot of a fully initialized execution environment (memory and disk state, post-init) and caches it. When a new instance is needed, Lambda resumes from that snapshot instead of running your init code from scratch.&lt;/p&gt;

&lt;p&gt;The practical result: init-phase latency drops by 70 to 90 percent for supported runtimes, according to AWS's own numbers. For a Java function with a heavy Spring context to boot, that's the difference between a multi-second cold start and something that barely registers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling it
&lt;/h2&gt;

&lt;p&gt;For a supported runtime, it's a one-line change in your function configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda update-function-configuration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; my-java-function &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--snap-start&lt;/span&gt; &lt;span class="nv"&gt;ApplyOn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;PublishedVersions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;ApplyOn=PublishedVersions&lt;/code&gt; part. SnapStart only applies to published versions of your function, not &lt;code&gt;$LATEST&lt;/code&gt;. That trips people up the first time, since testing against &lt;code&gt;$LATEST&lt;/code&gt; in the console won't show any SnapStart benefit at all.&lt;/p&gt;

&lt;p&gt;Via SAM or CDK, it looks like this (CDK, Python):&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aws_lambda&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;lambda_&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyFunction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JAVA_21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.example.Handler::handleRequest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;build/function.zip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;snap_start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SnapStartConf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ON_PUBLISHED_VERSIONS&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;h2&gt;
  
  
  The gotcha nobody warns you about: uniqueness
&lt;/h2&gt;

&lt;p&gt;Snapshots capture your execution environment's state, including anything you initialized before the handler runs, like a random seed, a cached secret, or a database connection. If your init code generates something that's supposed to be unique per execution environment (a UUID used as an instance identifier, for example) and you generate it once outside the handler, every resumed snapshot will have the exact same value, because they're all resuming from the identical frozen state.&lt;/p&gt;

&lt;p&gt;AWS's guidance here is specific: anything that needs to be unique per invocation or per environment needs to be regenerated inside the handler, or you need to use the Lambda runtime hooks (&lt;code&gt;beforeCheckpoint&lt;/code&gt; and &lt;code&gt;afterRestore&lt;/code&gt; for Java) to refresh things like connections and credentials on resume rather than assuming init-time state stays valid forever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Java: refreshing a connection on restore using runtime hooks&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.amazonaws.services.lambda.runtime.CoreLifecycleHooks&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Handler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;dbConnection&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;CoreLifecycleHooks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerBeforeCheckpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;beforeCheckpoint&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;CoreLifecycleHooks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerAfterRestore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;afterRestore&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterRestore&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// re-establish anything that shouldn't be shared across resumed snapshots&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dbConnection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createFreshConnection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Java 25 made this better
&lt;/h2&gt;

&lt;p&gt;Java 25 on Lambda changed how tiered compilation interacts with SnapStart and Provisioned Concurrency. Previously, SnapStart would stop compilation optimization at tier C1. Starting with Java 25, that cap is gone, so functions get the benefit of full JIT optimization even when running from a SnapStart snapshot, instead of being stuck at a less-optimized compilation tier indefinitely.&lt;/p&gt;

&lt;p&gt;If you're on an older Java version specifically because you assumed SnapStart's compilation ceiling was a permanent limitation, it's worth re-checking against Java 25.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's NOT supported
&lt;/h2&gt;

&lt;p&gt;Worth knowing before you go looking for it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Container image functions&lt;/strong&gt; don't support SnapStart, only zip-based deployments on supported managed runtimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js and Ruby runtimes&lt;/strong&gt; aren't supported as of this writing; only Java 11+, Python 3.12+, and .NET 8+.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OS-only (provided.al2/al2023) runtimes&lt;/strong&gt; aren't supported either.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pairing it with Graviton
&lt;/h2&gt;

&lt;p&gt;SnapStart cuts init latency. Graviton (arm64) instances cut execution cost and generally improve throughput for CPU-bound workloads. They're independent levers, and stacking both together is a reasonable default for any new Java or Python Lambda function where cold start and cost both matter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# SAM template snippet&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MyFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;java21&lt;/span&gt;
      &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arm64&lt;/span&gt;
      &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Should you turn this on everywhere
&lt;/h2&gt;

&lt;p&gt;For Java functions specifically, yes, almost by default, given how much init-phase latency Java pays compared to something like Node. For Python and .NET, it's still worth it if you're seeing meaningful cold start impact on user-facing latency, but the win is smaller since those runtimes generally cold-start faster to begin with.&lt;/p&gt;

&lt;p&gt;The one thing I'd actually test before flipping it on for a production function: run your function through a few resume cycles and check for any state that quietly stayed frozen from the snapshot when you expected it to be fresh. That's the failure mode that doesn't show up in a quick smoke test, only in production traffic days or weeks later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html" rel="noopener noreferrer"&gt;Improving startup performance with Lambda SnapStart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/aws/aws-lambda-snapstart-for-python-and-net-functions-is-now-generally-available/" rel="noopener noreferrer"&gt;AWS Lambda SnapStart for Python and .NET functions is now generally available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/compute/aws-lambda-now-supports-java-25/" rel="noopener noreferrer"&gt;AWS Lambda now supports Java 25&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
      <category>performance</category>
    </item>
    <item>
      <title>Mastering Columnstore Indexes in SQL Server: New Features and Performance Enhancements in</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Wed, 11 Jun 2025 15:29:28 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-1c31</link>
      <guid>https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-1c31</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-2022-58d6" class="crayons-story__hidden-navigation-link"&gt;Mastering Columnstore Indexes in SQL Server: New Features and Performance Enhancements in 2022&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/arvind_toorpu" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png" alt="arvind_toorpu profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/arvind_toorpu" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Arvind Toorpu
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Arvind Toorpu
                
              
              &lt;div id="story-author-preview-content-2242976" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/arvind_toorpu" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Arvind Toorpu&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-2022-58d6" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 18 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-2022-58d6" id="article-link-2242976"&gt;
          Mastering Columnstore Indexes in SQL Server: New Features and Performance Enhancements in 2022
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-2022-58d6" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/arvind_toorpu/mastering-columnstore-indexes-in-sql-server-new-features-and-performance-enhancements-in-2022-58d6#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>sql</category>
      <category>sqlserver</category>
      <category>performance</category>
      <category>database</category>
    </item>
    <item>
      <title>Make the most of the SQL Server Max DOP parameter to enhance your query run time by embracing parallelism!</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Wed, 11 Jun 2025 15:22:33 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/make-the-most-of-the-sql-server-max-dop-parameter-to-enhance-your-query-run-time-by-embracing-2a0l</link>
      <guid>https://dev.to/arvind_toorpu/make-the-most-of-the-sql-server-max-dop-parameter-to-enhance-your-query-run-time-by-embracing-2a0l</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg" class="crayons-story__hidden-navigation-link"&gt;Optimizing SQL Server Index Rebuilds with MAXDOP&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/arvind_toorpu" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png" alt="arvind_toorpu profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/arvind_toorpu" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Arvind Toorpu
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Arvind Toorpu
                
              
              &lt;div id="story-author-preview-content-2327874" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/arvind_toorpu" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Arvind Toorpu&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 7 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg" id="article-link-2327874"&gt;
          Optimizing SQL Server Index Rebuilds with MAXDOP
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/aws"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;aws&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dba"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dba&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>aws</category>
      <category>sqlserver</category>
      <category>database</category>
      <category>dba</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Fri, 23 May 2025 18:43:24 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/-2nj6</link>
      <guid>https://dev.to/arvind_toorpu/-2nj6</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g" class="crayons-story__hidden-navigation-link"&gt;Troubleshooting tempdb Growth: Identifying Idle Sessions Holding Temporary Resources in SQL Server&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/arvind_toorpu" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png" alt="arvind_toorpu profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/arvind_toorpu" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Arvind Toorpu
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Arvind Toorpu
                
              
              &lt;div id="story-author-preview-content-2250579" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/arvind_toorpu" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1933970%2Ff4ee48ef-ebd9-4c2a-94c1-845642bf3996.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Arvind Toorpu&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 25 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g" id="article-link-2250579"&gt;
          Troubleshooting tempdb Growth: Identifying Idle Sessions Holding Temporary Resources in SQL Server
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/aws"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;aws&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dba"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dba&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>aws</category>
      <category>database</category>
      <category>sqlserver</category>
      <category>dba</category>
    </item>
    <item>
      <title>Optimizing SQL Server Index Rebuilds with MAXDOP</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Wed, 07 May 2025 11:30:00 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg</link>
      <guid>https://dev.to/arvind_toorpu/optimizing-sql-server-index-rebuilds-with-maxdop-4flg</guid>
      <description>&lt;h1&gt;
  
  
  Optimizing SQL Server Index Rebuilds with MAXDOP: A Practical Guide
&lt;/h1&gt;

&lt;p&gt;Keeping your SQL Server databases running at peak performance requires regular index maintenance. One critical but often overlooked aspect is how &lt;strong&gt;parallelism&lt;/strong&gt;, controlled through the &lt;code&gt;MAXDOP&lt;/code&gt; setting, can significantly impact the speed and efficiency of index rebuilds. In this guide, we'll walk through how you can optimize your SQL Server index maintenance by properly configuring &lt;code&gt;MAXDOP&lt;/code&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.amazonaws.com%2Fuploads%2Farticles%2F8yihphhlrn0nezr74qwk.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%2F8yihphhlrn0nezr74qwk.png" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Index Maintenance Matters
&lt;/h2&gt;

&lt;p&gt;Index fragmentation is an inevitable side effect of everyday database operations like inserts, updates, and deletes. Over time, fragmented indexes can lead to slower queries and increased I/O, affecting the overall performance of your applications.&lt;/p&gt;

&lt;p&gt;Performing regular index maintenance — through &lt;strong&gt;rebuilds&lt;/strong&gt; or &lt;strong&gt;reorganizations&lt;/strong&gt; — is essential to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce query response times&lt;/li&gt;
&lt;li&gt;Improve system throughput&lt;/li&gt;
&lt;li&gt;Maintain efficient storage usage&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/indexes/reorganize-and-rebuild-indexes?view=sql-server-ver16" rel="noopener noreferrer"&gt;Learn more about index maintenance best practices from SQL Server Docs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Harnessing the Power of Parallelism with MAXDOP
&lt;/h2&gt;

&lt;p&gt;SQL Server can utilize multiple processors when performing index operations. The &lt;strong&gt;Maximum Degree of Parallelism (MAXDOP)&lt;/strong&gt; setting controls how many CPU cores SQL Server can use for these operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting MAXDOP During Index Rebuilds
&lt;/h3&gt;

&lt;p&gt;By default, index operations follow the instance-level &lt;code&gt;MAXDOP&lt;/code&gt; setting. However, you can explicitly override this for specific rebuilds:&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;INDEX&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IX_Sales_OrderDate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REBUILD&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command instructs SQL Server to use 4 processor cores for rebuilding the index, regardless of the server’s default &lt;code&gt;MAXDOP&lt;/code&gt; setting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reference: &lt;a href="https://www.mssqltips.com/sqlservertip/3100/reduce-time-for-sql-server-index-rebuilds-and-update-statistics/" rel="noopener noreferrer"&gt;Reduce Time for SQL Server Index Rebuilds and Update Statistics&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Benchmark Results: How MAXDOP Affects Rebuild Times
&lt;/h2&gt;

&lt;p&gt;To illustrate the real-world impact, I ran tests on a large table containing 15 million rows (~60GB) using different &lt;code&gt;MAXDOP&lt;/code&gt; values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Setup:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL Server&lt;/strong&gt;: 2022 Enterprise Edition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cores&lt;/strong&gt;: 16 logical processors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt;: 128GB RAM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: NVMe SSD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table&lt;/strong&gt;: SalesTransactions (60GB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index&lt;/strong&gt;: IX_SalesTransactions_TransactionDate (Non-clustered)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Results:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MAXDOP Setting&lt;/th&gt;
&lt;th&gt;Rebuild Duration (seconds)&lt;/th&gt;
&lt;th&gt;CPU Utilization (%)&lt;/th&gt;
&lt;th&gt;Memory Usage (GB)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;420&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;2.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;4.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;135&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;7.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;12.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;td&gt;12.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;12.8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaway&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Significant performance improvements were observed up to &lt;code&gt;MAXDOP = 8&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Beyond 8, performance gains were marginal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This aligns with Microsoft's guidance to limit &lt;code&gt;MAXDOP&lt;/code&gt; to &lt;strong&gt;8 or fewer&lt;/strong&gt; for most workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical MAXDOP Configuration Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: 24x7 Production System
&lt;/h3&gt;

&lt;p&gt;In environments with continuous user activity, balance is crucial.&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;INDEX&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IX_CustomerTransactions_TransactionDate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;CustomerTransactions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REBUILD&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ONLINE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Moderate parallelism ensures faster rebuilds without heavily impacting user workloads.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ONLINE = ON&lt;/code&gt; allows queries to continue during the rebuild.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scenario 2: Dedicated Maintenance Window
&lt;/h3&gt;

&lt;p&gt;If you have a maintenance window with minimal load:&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;INDEX&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;OrderDetails&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REBUILD&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Higher parallelism maximizes rebuild speed.&lt;/li&gt;
&lt;li&gt;Ideal when users are offline or during planned downtimes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scenario 3: Resource-Constrained Systems
&lt;/h3&gt;

&lt;p&gt;On systems with limited CPU or concurrent heavy usage:&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;INDEX&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IX_Inventory_ProductID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Inventory&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REORGANIZE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PK_Inventory_Products&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Inventory&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REBUILD&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Prefer &lt;strong&gt;reorganization&lt;/strong&gt; over &lt;strong&gt;rebuild&lt;/strong&gt; when possible.&lt;/li&gt;
&lt;li&gt;Limit &lt;code&gt;MAXDOP&lt;/code&gt; for minimal disruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Reorganize operations are &lt;strong&gt;single-threaded&lt;/strong&gt; by design.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Best Practices for Setting MAXDOP
&lt;/h2&gt;

&lt;p&gt;When fine-tuning &lt;code&gt;MAXDOP&lt;/code&gt; for index operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with &lt;code&gt;MAXDOP = 0.5 × (physical cores)&lt;/code&gt; (up to 8).&lt;/li&gt;
&lt;li&gt;Monitor system waits and resource usage during rebuilds.&lt;/li&gt;
&lt;li&gt;For OLTP systems, restrict &lt;code&gt;MAXDOP&lt;/code&gt; to 4 or fewer during peak hours.&lt;/li&gt;
&lt;li&gt;Evaluate your storage subsystem — parallelism increases I/O!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example configuration for a 16-core server:&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;INDEX&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IX_FactInternetSales_OrderDate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;FactInternetSales&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REBUILD&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SORT_IN_TEMPDB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SORT_IN_TEMPDB = ON&lt;/code&gt; offloads sorting operations, reducing contention in user databases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Important Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Edition&lt;/strong&gt; is required for parallel index rebuilds. Standard Edition operations remain single-threaded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reorganize operations&lt;/strong&gt; do not use multiple threads.&lt;/li&gt;
&lt;li&gt;Online rebuilds with &lt;code&gt;ALLOW_PAGE_LOCKS = OFF&lt;/code&gt; and &lt;code&gt;MAXDOP &amp;gt; 1&lt;/code&gt; can lead to increased fragmentation.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;MAXDOP&lt;/code&gt; exceeds available CPUs, SQL Server uses the maximum available automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Validate MAXDOP Settings
&lt;/h2&gt;

&lt;p&gt;You can confirm your MAXDOP usage by capturing execution plans:&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;EVENT&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CapturePlans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;EVENT&lt;/span&gt; &lt;span class="n"&gt;sqlserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_post_execution_showplan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ACTION&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sql_text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="n"&gt;package0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s1"&gt;emp&lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s1"&gt;apturePlans.xel'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_MEMORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt; &lt;span class="n"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EVENT_RETENTION_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ALLOW_SINGLE_EVENT_LOSS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;MAX_DISPATCH_LATENCY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="n"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STARTUP_STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;OFF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;EVENT&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CapturePlans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="k"&gt;STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Execute your index rebuild&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IX_Sales_CustomerID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REBUILD&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;EVENT&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CapturePlans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="k"&gt;STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;STOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analyze the captured event file to review the degree of parallelism utilized.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Tuning &lt;code&gt;MAXDOP&lt;/code&gt; for index rebuilds is a powerful way to optimize SQL Server maintenance without sacrificing user experience. With careful configuration based on your environment's needs, you can dramatically reduce maintenance windows and keep your databases performing at their best.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sqlserver</category>
      <category>database</category>
      <category>dba</category>
    </item>
    <item>
      <title>Troubleshooting tempdb Growth: Identifying Idle Sessions Holding Temporary Resources in SQL Server</title>
      <dc:creator>Arvind Toorpu</dc:creator>
      <pubDate>Fri, 25 Apr 2025 17:00:40 +0000</pubDate>
      <link>https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g</link>
      <guid>https://dev.to/arvind_toorpu/troubleshooting-tempdb-growth-identifying-idle-sessions-holding-temporary-resources-in-sql-server-3j2g</guid>
      <description>&lt;p&gt;To find out how much &lt;strong&gt;tempdb&lt;/strong&gt; disk space is occupied by each session that is connected and is idle in a SQL Server database, you can use the following query. This query leverages Dynamic Management Views (DMVs) like &lt;code&gt;sys.dm_db_session_space_usage&lt;/code&gt; and &lt;code&gt;sys.dm_exec_sessions&lt;/code&gt; to identify idle sessions and their &lt;strong&gt;tempdb&lt;/strong&gt; usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query to Find &lt;strong&gt;tempdb&lt;/strong&gt; Disk Space Usage by Idle Sessions:
&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="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;program_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_objects_alloc_page_count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_objects_alloc_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_objects_dealloc_page_count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_objects_dealloc_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internal_objects_alloc_page_count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;internal_objects_alloc_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internal_objects_dealloc_page_count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;internal_objects_dealloc_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_objects_alloc_page_count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_objects_dealloc_page_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_objects_net_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internal_objects_alloc_page_count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internal_objects_dealloc_page_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;internal_objects_net_mb&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; 
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dm_db_session_space_usage&lt;/span&gt; &lt;span class="n"&gt;su&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; 
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dm_exec_sessions&lt;/span&gt; &lt;span class="n"&gt;es&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; 
    &lt;span class="n"&gt;es&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sleeping'&lt;/span&gt; &lt;span class="c1"&gt;-- Filter for idle sessions&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_objects_alloc_page_count&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="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;su&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internal_objects_alloc_page_count&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="c1"&gt;-- Filter for sessions using tempdb&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;user_objects_net_mb&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;internal_objects_net_mb&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation of the Query:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sys.dm_db_session_space_usage&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracks &lt;strong&gt;tempdb&lt;/strong&gt; space usage for each session.&lt;/li&gt;
&lt;li&gt;Columns like &lt;code&gt;user_objects_alloc_page_count&lt;/code&gt; and &lt;code&gt;internal_objects_alloc_page_count&lt;/code&gt; show the number of 8 KB pages allocated for user objects (e.g., temporary tables) and internal objects (e.g., worktables).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sys.dm_exec_sessions&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides session-level information, such as &lt;code&gt;session_id&lt;/code&gt;, &lt;code&gt;login_name&lt;/code&gt;, &lt;code&gt;host_name&lt;/code&gt;, &lt;code&gt;program_name&lt;/code&gt;, and &lt;code&gt;status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;status&lt;/code&gt; column is used to filter for idle sessions (&lt;code&gt;status = 'sleeping'&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Calculations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiply page counts by &lt;code&gt;8 / 1024.0&lt;/code&gt; to convert pages to megabytes (MB).&lt;/li&gt;
&lt;li&gt;Calculate the &lt;strong&gt;net&lt;/strong&gt; space usage by subtracting deallocated pages from allocated pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Filters&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;es.status = 'sleeping'&lt;/code&gt;: Filters for sessions that are idle (not actively running queries).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;su.user_objects_alloc_page_count &amp;gt; 0 OR su.internal_objects_alloc_page_count &amp;gt; 0&lt;/code&gt;: Ensures only sessions using &lt;strong&gt;tempdb&lt;/strong&gt; are included.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;user_objects_net_mb&lt;/code&gt;&lt;/strong&gt;: Net space used by user objects (e.g., temporary tables) in MB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;internal_objects_net_mb&lt;/code&gt;&lt;/strong&gt;: Net space used by internal objects (e.g., worktables) in MB.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Output:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;session_id&lt;/th&gt;
&lt;th&gt;login_name&lt;/th&gt;
&lt;th&gt;host_name&lt;/th&gt;
&lt;th&gt;program_name&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;user_objects_alloc_mb&lt;/th&gt;
&lt;th&gt;user_objects_dealloc_mb&lt;/th&gt;
&lt;th&gt;internal_objects_alloc_mb&lt;/th&gt;
&lt;th&gt;internal_objects_dealloc_mb&lt;/th&gt;
&lt;th&gt;user_objects_net_mb&lt;/th&gt;
&lt;th&gt;internal_objects_net_mb&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;sa&lt;/td&gt;
&lt;td&gt;SQLHost01&lt;/td&gt;
&lt;td&gt;SQLServerManagement&lt;/td&gt;
&lt;td&gt;sleeping&lt;/td&gt;
&lt;td&gt;50.25&lt;/td&gt;
&lt;td&gt;10.00&lt;/td&gt;
&lt;td&gt;20.50&lt;/td&gt;
&lt;td&gt;5.00&lt;/td&gt;
&lt;td&gt;40.25&lt;/td&gt;
&lt;td&gt;15.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67&lt;/td&gt;
&lt;td&gt;app_user&lt;/td&gt;
&lt;td&gt;AppServer01&lt;/td&gt;
&lt;td&gt;MyApp.exe&lt;/td&gt;
&lt;td&gt;sleeping&lt;/td&gt;
&lt;td&gt;30.00&lt;/td&gt;
&lt;td&gt;30.00&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Notes:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Idle sessions (&lt;code&gt;status = 'sleeping'&lt;/code&gt;) may still hold &lt;strong&gt;tempdb&lt;/strong&gt; resources if they have not explicitly dropped temporary objects or if their scope has not ended.&lt;/li&gt;
&lt;li&gt;If you notice high &lt;strong&gt;tempdb&lt;/strong&gt; usage by idle sessions, investigate whether temporary objects are being properly cleaned up or if long-running transactions are holding resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This query is a great way to monitor and troubleshoot &lt;strong&gt;tempdb&lt;/strong&gt; usage, especially in environments with high concurrency or large temporary object usage.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>sqlserver</category>
      <category>dba</category>
    </item>
  </channel>
</rss>
