<?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: William Coates</title>
    <description>The latest articles on DEV Community by William Coates (@william_coates_b1066e7cae).</description>
    <link>https://dev.to/william_coates_b1066e7cae</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%2F2089082%2F9ce3e61d-fa92-44c5-894a-666e6975ccc3.jpg</url>
      <title>DEV Community: William Coates</title>
      <link>https://dev.to/william_coates_b1066e7cae</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/william_coates_b1066e7cae"/>
    <language>en</language>
    <item>
      <title>AWS Database Savings Plans: Panacea or Pain in the Bum?</title>
      <dc:creator>William Coates</dc:creator>
      <pubDate>Sat, 11 Jul 2026 09:56:19 +0000</pubDate>
      <link>https://dev.to/william_coates_b1066e7cae/aws-database-savings-plans-panacea-or-pain-in-the-bum-1fgn</link>
      <guid>https://dev.to/william_coates_b1066e7cae/aws-database-savings-plans-panacea-or-pain-in-the-bum-1fgn</guid>
      <description>&lt;p&gt;AWS announced Database Savings Plans in December 2025 as a panacea to your database spending woes: commit to a fixed hourly dollar amount, and get up to 35% off usage across almost every managed database service you run — Aurora, RDS, DynamoDB, ElastiCache, DocumentDB, Neptune, Keyspaces, Timestream, DMS, and (since March 2026) OpenSearch. One commitment, sixteen services, no upfront payment.&lt;/p&gt;

&lt;p&gt;That sounds amazing, but unfortunately, you're gonna be hard pushed to cut 35% — or even 20% — off your database spending, thanks to some sneaky little caveats that Amazon doesn't make immediately obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pitch: sixteen services, one commitment
&lt;/h2&gt;

&lt;p&gt;Open the &lt;a href="https://aws.amazon.com/savingsplans/database-pricing/" rel="noopener noreferrer"&gt;Database Savings Plans pricing page&lt;/a&gt; and you'll find usage broken across the services covered by the plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aurora and RDS Instances&lt;/li&gt;
&lt;li&gt;Aurora Serverless v2&lt;/li&gt;
&lt;li&gt;Aurora DSQL&lt;/li&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;li&gt;ElastiCache for Valkey Instances&lt;/li&gt;
&lt;li&gt;ElastiCache for Valkey Serverless&lt;/li&gt;
&lt;li&gt;Amazon DocumentDB Instances / Serverless&lt;/li&gt;
&lt;li&gt;Neptune Instances / Serverless / Analytics&lt;/li&gt;
&lt;li&gt;Keyspaces&lt;/li&gt;
&lt;li&gt;Timestream&lt;/li&gt;
&lt;li&gt;DMS Instances / Serverless&lt;/li&gt;
&lt;li&gt;Amazon OpenSearch Service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're basically just committed to spending a certain dollar amount per hour for the whole year. And it doesn't matter if you spin up new databases, spin down old ones, move workloads between services, or switch regions — the saving remains. Unfortunately, the devil's in the detail — because it only covers specific instance types.&lt;/p&gt;

&lt;h2&gt;
  
  
  The catch: only newer and more expensive instance families
&lt;/h2&gt;

&lt;p&gt;The pain in the bum that AWS doesn't advertise is that it only supports &lt;strong&gt;Gen 7 and newer&lt;/strong&gt; instance families. Scan the full RDS/Aurora list and you won't find a single row for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;db.t4g&lt;/strong&gt; — AWS's own recommended burstable family for dev/test and small production workloads, and Graviton2-based, not some ancient relic&lt;/li&gt;
&lt;li&gt;db.t3 / db.t3g&lt;/li&gt;
&lt;li&gt;db.m5 / db.r5&lt;/li&gt;
&lt;li&gt;db.m6 / db.r6 (non-Graviton or earlier Graviton generations)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a floor on size, not just generation — no savings for us peasants on the smaller instance types. Scan the eligible Gen 7+ families and the smallest size you'll find is &lt;code&gt;.large&lt;/code&gt;; there's no &lt;code&gt;.medium&lt;/code&gt;, &lt;code&gt;.small&lt;/code&gt;, or &lt;code&gt;.micro&lt;/code&gt; variant anywhere in the m7g/r7g/m8g/r8g/m9g lineup. If you're running anything that's excluded from the plan it will never apply to that instance.&lt;/p&gt;

&lt;p&gt;And this isn't an RDS oddity — AWS's own Savings Plans FAQ states plainly that Database Savings Plans only offer coverage for Generation 7 and newer instances. So the same exclusions apply to DocumentDB, Neptune, OpenSearch, and DMS too. Any usage on those services that's still pre-Gen-7 gets zero benefit from a Database Savings Plan commitment, just like RDS.&lt;/p&gt;

&lt;p&gt;ElastiCache adds an extra restriction on top: coverage is Valkey-only, so even a Gen 7+ Redis OSS or Memcached node doesn't qualify — you need both the newer generation &lt;em&gt;and&lt;/em&gt; the Valkey engine. Timestream has its own narrow scope too, limited to InfluxDB instances specifically.&lt;/p&gt;

&lt;p&gt;That last point about T-class is worth mulling over. T-class instances are how a huge number of teams run dev, staging, QA, and small production databases — precisely the "not core revenue, but still on the AWS bill every month" workloads that make ideal Savings Plan candidates. Under Database Savings Plans, none of that spend is eligible, no matter how large a commitment you buy — and the same logic extends to any pre-Gen-7 instance sitting inside DocumentDB, Neptune, OpenSearch, or DMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless Savings Baby
&lt;/h2&gt;

&lt;p&gt;The two services that dodge this entirely are DynamoDB and Keyspaces — they're throughput/capacity-based rather than instance-based, so there's no "generation" to be excluded from. And every serverless offering gets the same free pass: Aurora Serverless v2, Aurora DSQL, ElastiCache Serverless for Valkey, DocumentDB Serverless, Neptune Serverless, DMS Serverless, and OpenSearch Serverless are all covered from the first hour you spin them up, with no instance-generation check to clear. If your workload lives on any of these, the generational cliff simply doesn't apply to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to think about before you commit
&lt;/h2&gt;

&lt;p&gt;Database Savings Plans are a fixed 1-year commitment. There's no early exit and no ability to reduce it if your workload composition doesn't match what you assumed. That makes the coverage gap a real financial risk, not a footnote:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If your stack is mostly Gen 7+ already&lt;/strong&gt;, this is close to a no-brainer — flat 20%+ savings with total flexibility to change instance type, engine, or region without losing the discount.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're spending a lot on t4g, t3, m5, or r5&lt;/strong&gt;, that portion gets zero benefit from a Database Savings Plan commitment. You'd either need to upgrade those instances to a Gen 7+ family first (itself a cost change, before any discount applies) or accept that your commitment will be sized against a smaller pool of eligible usage than your total database bill suggests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There's no t-class equivalent in the Gen 7+ range&lt;/strong&gt; — AWS hasn't shipped a burstable t7g/t8g/t9g family, so "just upgrade the generation" isn't a drop-in option here the way it is for Aurora r7g → r8g. Moving off T-class means moving to a non-burstable family entirely, which is a different cost and performance profile, not a like-for-like swap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If a decent portion of your bill is already spent on the eligible serverless services&lt;/strong&gt;, the sizing exercise is much simpler: just baseline your consistent monthly spend on those serverless databases and commit against that floor. No generation-checking required — just find the level your serverless usage never drops below, month over month, and commit there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The practical takeaway
&lt;/h2&gt;

&lt;p&gt;Before sizing a Database Savings Plan commitment, don't just total your eligible-sounding database spend from Cost Explorer — cross-reference it against the actual instance types you're running. A commitment built against your full database bill, without checking generational eligibility first, will systematically overestimate your effective coverage. The gap between "total database spend" and "Database Savings Plan-eligible database spend" is exactly the kind of thing that's invisible until the first invoice lands with a lower discount than expected.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Curious whether your own AWS account has hidden non-eligible instances sitting inside what looks like Database Savings Plan-covered spend? &lt;a href="https://junglecleaner.com" rel="noopener noreferrer"&gt;Jungle Cleaner&lt;/a&gt; checks for exactly this kind of coverage gap automatically.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>serverless</category>
    </item>
    <item>
      <title>OpenSearch Serverless NextGen: What's New, and How to Migrate from Classic</title>
      <dc:creator>William Coates</dc:creator>
      <pubDate>Thu, 09 Jul 2026 07:31:58 +0000</pubDate>
      <link>https://dev.to/william_coates_b1066e7cae/opensearch-serverless-nextgen-whats-new-and-how-to-migrate-from-classic-4e63</link>
      <guid>https://dev.to/william_coates_b1066e7cae/opensearch-serverless-nextgen-whats-new-and-how-to-migrate-from-classic-4e63</guid>
      <description>&lt;p&gt;AWS has rolled out a new generation of OpenSearch Serverless. If you're currently on Classic collections, it's worth understanding what's actually changed before deciding whether — and how — to move.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with Classic
&lt;/h2&gt;

&lt;p&gt;OpenSearch Serverless Classic was a great way to get started with OpenSearch without managing cluster sizing yourself. But it had one expensive catch: the &lt;strong&gt;minimum indexing and search capacity was fixed at 2 OCUs each&lt;/strong&gt;, regardless of whether your index was doing anything. (An OCU, or OpenSearch Compute Unit, is the billing unit AWS uses for serverless compute and memory capacity — you're charged per OCU-hour whether or not it's actually processing anything.) For an always-on production workload that's a reasonable floor. For a staging environment, a side project, or anything with bursty or intermittent traffic, you're paying a constant baseline cost 24/7 for capacity you're mostly not using.&lt;/p&gt;

&lt;h2&gt;
  
  
  What NextGen changes
&lt;/h2&gt;

&lt;p&gt;The headline feature: &lt;strong&gt;NextGen can scale down to zero&lt;/strong&gt;. If nothing's querying or indexing, you stop paying for that capacity.&lt;/p&gt;

&lt;p&gt;There's a real trade-off to know about before you migrate: scaling up from zero isn't instant. If your search capacity has scaled to zero, the first request back can see latency in the &lt;strong&gt;tens of seconds&lt;/strong&gt; while it spins back up. For dev/staging that's a non-issue. For production, you'll want to keep at least &lt;strong&gt;1 unit of search capacity always on&lt;/strong&gt; so user-facing queries never hit a cold start — you can still let indexing scale to zero if ingestion is intermittent.&lt;/p&gt;

&lt;p&gt;The upside on the scaling side isn't just the zero floor. AWS reports NextGen scales roughly &lt;strong&gt;20x faster&lt;/strong&gt; than Classic, in both directions — it reacts to load spikes faster, and it scales back down faster too, which is what actually turns "can scale to zero" into "spends most of its time at zero" instead of getting stuck at higher capacity from residual traffic.&lt;/p&gt;

&lt;p&gt;NextGen also lets you set minimum and maximum capacity independently for indexing and search. So rather than a single fixed floor applying to both, you can, for example, let indexing scale to zero while keeping a minimum of 1 OCU on search — or cap maximum capacity on either side if you want a hard ceiling on cost regardless of load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for cost
&lt;/h2&gt;

&lt;p&gt;In short: you're no longer stuck paying for a fixed floor of 2+2 OCUs whether you need it or not. You pay for roughly what you actually use. That's most impactful for dev, staging, and any environment with uneven or intermittent traffic — the exact cases where Classic's fixed minimum was hardest to justify.&lt;/p&gt;

&lt;p&gt;Whether this is actually worth doing depends on your traffic pattern, though — a consistently busy production index may not see much of a difference. If you want a quick look at whether you'd see real savings before committing to a migration, you can run the &lt;a href="https://junglecleaner.com" rel="noopener noreferrer"&gt;Jungle Cleaner&lt;/a&gt; MCP scan tool against your account — it looks at your current OpenSearch Serverless usage and estimates what you'd save by moving to NextGen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fux9t8jltvqukyp0m59t1.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%2Fux9t8jltvqukyp0m59t1.png" alt="Jungle Cleaner MCP scan running in Claude Code" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the migration isn't a simple upgrade
&lt;/h2&gt;

&lt;p&gt;There's no in-place upgrade path. Collection generation (Classic vs. NextGen) is immutable in OpenSearch Serverless — you can't flip a setting on an existing collection. You have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a brand-new NextGen collection group, then a collection inside it (with its own access, network, and encryption policies)&lt;/li&gt;
&lt;li&gt;Copy your data across&lt;/li&gt;
&lt;li&gt;Point your application at the new endpoint&lt;/li&gt;
&lt;li&gt;Delete the old Classic collection once you've confirmed the cutover worked&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AWS doesn't provide a first-party tool for step 2. Their own recommendation for cross-collection copies is to stand up an OpenSearch Ingestion pipeline — which means an IAM role, a data access policy, pipeline YAML, and its own OCU capacity, just for a one-time copy job. That's a lot of infrastructure for something you'll run once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the new NextGen group and collection
&lt;/h2&gt;

&lt;p&gt;We've put together a simple example stack — a single Lambda connecting to the index — showing how to provision a NextGen collection with sensible defaults (access policy, network policy, encryption policy): &lt;strong&gt;&lt;a href="https://github.com/JungleCleaner/aws-cleanup-scripts/blob/main/infra-examples/opensearch-nextgen.yaml" rel="noopener noreferrer"&gt;opensearch-nextgen.yaml&lt;/a&gt;&lt;/strong&gt;. If you're setting this up with a coding agent, it's worth pointing it at our template as a guide rather than letting it write the policies from scratch — agents often trip up on NextGen because their training data cuts off before these settings existed, so they'll confidently reach for Classic-era configuration that doesn't apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating the data
&lt;/h2&gt;

&lt;p&gt;Once your NextGen collection exists and is up and running, use our open source migration script to copy everything over:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/JungleCleaner/aws-cleanup-scripts/blob/main/migrate-opensearch-classic-to-nextgen.py" rel="noopener noreferrer"&gt;migrate-opensearch-classic-to-nextgen.py&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few things worth knowing about how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;_reindex&lt;/code&gt;.&lt;/strong&gt; Serverless doesn't support &lt;code&gt;_reindex&lt;/code&gt; with a remote source, so the script copies documents directly — opening a Point-in-Time on the source index, paginating with &lt;code&gt;search_after&lt;/code&gt;, and writing batches via &lt;code&gt;_bulk&lt;/code&gt;. This is the same fundamental approach an Ingestion pipeline uses under the hood, minus the setup overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's idempotent.&lt;/strong&gt; Documents are written to the target with their original &lt;code&gt;_id&lt;/code&gt;, so re-running the script never creates duplicates — it just overwrites with identical content. This matters in practice: if new data gets written to the old collection while you're mid-migration, you can point your application at the new collection and then simply run the script again to pick up anything that landed after your first pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mappings and analyzers come across too.&lt;/strong&gt; Custom field mappings and analyzers are copied before data, so the target index doesn't end up with OpenSearch guessing field types via dynamic mapping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failures don't kill the run.&lt;/strong&gt; Any document that fails after a retry gets logged to a separate file instead of aborting — one bad document won't block migrating the other few million.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basic usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 migrate-opensearch-classic-to-nextgen.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-collection-id&lt;/span&gt; &amp;lt;classic-collection-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-collection-id&lt;/span&gt; &amp;lt;existing-nextgen-collection-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;--dry-run&lt;/code&gt; first to see index and document counts without copying anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  After the migration
&lt;/h2&gt;

&lt;p&gt;Once you've cut your application over to the NextGen endpoint and confirmed traffic is flowing correctly, delete the old Classic collection to stop paying for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws opensearchserverless delete-collection &lt;span class="nt"&gt;--id&lt;/span&gt; &amp;lt;classic-collection-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To help you clean it up, use the &lt;a href="https://junglecleaner.com" rel="noopener noreferrer"&gt;Jungle Cleaner&lt;/a&gt; MCP cleanup tool, which will identify the exact commands you (or your agent) needs — it verifies the old Classic collection has had no read or write activity since your cutover before suggesting removal, so you're not deleting anything that's still quietly in use somewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk02wv89jjtgeevlj0av4.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%2Fk02wv89jjtgeevlj0av4.png" alt="Jungle Cleaner MCP cleanup tool verifying no activity before removal" width="799" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What's new in OpenSearch Serverless NextGen?&lt;/strong&gt;&lt;br&gt;
NextGen can scale indexing and search capacity down to zero, and scales roughly 20x faster than Classic in both directions. Classic had a fixed minimum of 2 OCUs each for indexing and search, regardless of usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I upgrade a Classic collection to NextGen in place?&lt;/strong&gt;&lt;br&gt;
No. Collection generation is immutable — you must create a new NextGen collection and migrate data into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will I lose data during migration?&lt;/strong&gt;&lt;br&gt;
Not if you follow the standard cutover order: create the new collection, run the migration script, verify data and application behavior against the new endpoint, and only then delete the old collection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if search scales to zero in production?&lt;/strong&gt;&lt;br&gt;
The first query after a cold start can take tens of seconds to respond. Keep at least 1 unit of search capacity provisioned in production to avoid this; indexing capacity is generally safer to let scale to zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the migration script support incremental re-runs?&lt;/strong&gt;&lt;br&gt;
Yes — it's idempotent by document ID, so re-running it after cutting over your application catches any writes that happened during the migration window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I install Jungle Cleaner?&lt;/strong&gt;&lt;br&gt;
Go to &lt;a href="https://junglecleaner.com" rel="noopener noreferrer"&gt;junglecleaner.com&lt;/a&gt; and follow the instructions there to install the MCP tool.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>serverless</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
