<?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: Sidharth Bangera</title>
    <description>The latest articles on DEV Community by Sidharth Bangera (@sidharth_bangera).</description>
    <link>https://dev.to/sidharth_bangera</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%2F4167066%2F2504cd58-3050-4161-95b8-d7e57c7c1685.jpg</url>
      <title>DEV Community: Sidharth Bangera</title>
      <link>https://dev.to/sidharth_bangera</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sidharth_bangera"/>
    <language>en</language>
    <item>
      <title>Simulating DynamoDB Costs in Tables: GSIs, Backups, Streams, and Replicas</title>
      <dc:creator>Sidharth Bangera</dc:creator>
      <pubDate>Sat, 10 Oct 2026 11:06:28 +0000</pubDate>
      <link>https://dev.to/sidharth_bangera/simulating-dynamodb-costs-in-tables-gsis-backups-streams-and-replicas-5h18</link>
      <guid>https://dev.to/sidharth_bangera/simulating-dynamodb-costs-in-tables-gsis-backups-streams-and-replicas-5h18</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When I evaluate a DynamoDB design change, I want to understand how it affects cost before I change the AWS resources themselves.&lt;/p&gt;

&lt;p&gt;In this walkthrough, I use &lt;a href="https://tables.serverlesscreed.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Tables by ServerlessCreed&lt;/strong&gt;&lt;/a&gt;, a DynamoDB GUI with cost analysis and simulation tools, to compare a baseline workload with several what-if changes: two global secondary indexes (GSIs), point-in-time recovery (PITR), backup storage, DynamoDB Streams reads, and two replica Regions.&lt;/p&gt;

&lt;p&gt;The test uses a fixed synthetic workload so that each cost driver can be added separately and compared with the previous state. The dollar values shown here are &lt;strong&gt;simulator estimates&lt;/strong&gt;, not actual AWS charges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To reproduce the workflow, you need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;1. &lt;a href="https://tables.serverlesscreed.com/" rel="noopener noreferrer"&gt;Tables by ServerlessCreed&lt;/a&gt; installed. The installer is available from the &lt;a href="https://tables.serverlesscreed.com/downloads" rel="noopener noreferrer"&gt;official download page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Access to a non-production DynamoDB environment.&lt;/li&gt;
&lt;li&gt;A table available in Tables for cost analysis.&lt;/li&gt;
&lt;li&gt;A known Region and a consistent simulation period.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this test, I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tables version:&lt;/strong&gt; &lt;code&gt;v3.3.37&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table:&lt;/strong&gt; &lt;code&gt;Export_Source_Test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary Region:&lt;/strong&gt; &lt;code&gt;us-east-1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simulation period:&lt;/strong&gt; 7 days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table items:&lt;/strong&gt; 100&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution:&lt;/strong&gt; local what-if simulation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The simulation changed only estimator inputs; it did not modify those AWS resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Starting from the observed cost baseline
&lt;/h2&gt;

&lt;p&gt;I first opened &lt;strong&gt;Cost Estimation&lt;/strong&gt; using the table’s observed metrics.&lt;/p&gt;

&lt;p&gt;Reads, writes, and table storage were available, but Streams reads and backup/PITR inputs were incomplete. Tables marked those components as incomplete instead of silently treating them as zero.&lt;/p&gt;

&lt;p&gt;For the 7-day observed view, the current and forecast cost remained below &lt;code&gt;$0.01&lt;/code&gt;. I used this only as the observed starting point; the detailed comparisons below use a fixed synthetic baseline.&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%2F3s8efsei358k94n09rl0.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%2F3s8efsei358k94n09rl0.png" alt="Tables Cost Optimizer for Export_Source_Test in us-east-1, showing a 7-day observed baseline with current spend below $0.01, forecast below $0.01, write amplification 1.0×, and no material savings found." width="800" height="402"&gt;&lt;/a&gt;&lt;em&gt;Observed cost baseline for Export_Source_Test. The 7-day Cost Optimizer view shows current and forecast spend below $0.01, write amplification of 1.0×, and no material savings identified before any simulation inputs were added.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Loading a fixed synthetic baseline
&lt;/h2&gt;

&lt;p&gt;To make the comparison repeatable, I loaded the built-in synthetic baseline.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Table storage&lt;/td&gt;
&lt;td&gt;10 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table read units&lt;/td&gt;
&lt;td&gt;6,048,000 over 7 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table write units&lt;/td&gt;
&lt;td&gt;3,024,000 over 7 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak reads&lt;/td&gt;
&lt;td&gt;20/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak writes&lt;/td&gt;
&lt;td&gt;10/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream GetRecords calls&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup storage&lt;/td&gt;
&lt;td&gt;0 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PITR&lt;/td&gt;
&lt;td&gt;Off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simulated GSIs&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replica Regions&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With those inputs, Tables produced these monthly estimates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Billing model&lt;/th&gt;
&lt;th&gt;Estimated monthly cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;On-Demand&lt;/td&gt;
&lt;td&gt;$13.84&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provisioned&lt;/td&gt;
&lt;td&gt;$10.47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reserved 1 year&lt;/td&gt;
&lt;td&gt;$6.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reserved 3 year&lt;/td&gt;
&lt;td&gt;$4.33&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the comparisons below, I used &lt;strong&gt;On-Demand&lt;/strong&gt; consistently so that each change was measured against the same billing model.&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%2Fv357ygv4ise2goia9tux.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%2Fv357ygv4ise2goia9tux.png" alt="Tables Cost Estimation synthetic baseline showing 10 GB storage, 6,048,000 read units, 3,024,000 write units, 20 peak reads per second, 10 peak writes per second, zero Stream GetRecords calls, zero backup storage, PITR off, and no GSIs or replicas." width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F51zy61a39zxgikcpbck5.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%2F51zy61a39zxgikcpbck5.png" alt="Tables Cost Estimation baseline pricing showing On-Demand $13.84, Provisioned $10.47, Reserved 1 year $6.16, and Reserved 3 year $4.33 per month." width="800" height="396"&gt;&lt;/a&gt;&lt;em&gt;Synthetic baseline inputs. I loaded a fixed 7-day workload with 10 GB of storage, 6,048,000 read units, 3,024,000 write units, no Streams activity, no backup storage, PITR disabled, and no simulated GSIs or replicas.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Comparing two simulated GSIs
&lt;/h2&gt;

&lt;p&gt;I next added two simulated GSIs with the same write volume but very different read activity.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;SimulatedGSI1&lt;/code&gt;, I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read units: 500,000 over 7 days
Write units: 3,024,000 over 7 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The On-Demand estimate increased from &lt;code&gt;$13.84&lt;/code&gt; to &lt;code&gt;$22.21/month&lt;/code&gt;, an increment of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+$8.37/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The breakdown showed approximately &lt;code&gt;$0.27/month&lt;/code&gt; for GSI reads and &lt;code&gt;$8.10/month&lt;/code&gt; for GSI writes.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;SimulatedGSI2&lt;/code&gt;, I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read units: 5,000 over 7 days
Write units: 3,024,000 over 7 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With both GSIs present, the estimate reached &lt;code&gt;$30.31/month&lt;/code&gt;. The second GSI added approximately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+$8.10/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fks457u3zynci8801151t.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%2Fks457u3zynci8801151t.png" alt="Tables GSI simulation showing two GSIs with different read volumes and the same write volume." width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82ipcjcce76ivocl6w1a.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%2F82ipcjcce76ivocl6w1a.png" alt="Tables GSI simulation showing two GSIs with different read volumes and the same write volume." width="799" height="490"&gt;&lt;/a&gt;&lt;em&gt;GSI simulation inputs. SimulatedGSI1 uses 500,000 read units and SimulatedGSI2 uses 5,000 read units over the 7-day period, while both use 3,024,000 write units.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35yi8iuqntah1kmg08zm.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%2F35yi8iuqntah1kmg08zm.png" alt="Tables cost breakdown showing both simulated GSIs and an On-Demand estimate of $30.31 per month, with GSI write costs dominating the index cost." width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3z8iz28r2v02g0m77oo.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%2Ff3z8iz28r2v02g0m77oo.png" alt="Tables cost breakdown showing both simulated GSIs and an On-Demand estimate of $30.31 per month, with GSI write costs dominating the index cost." width="800" height="572"&gt;&lt;/a&gt;&lt;em&gt;GSI cost result. With both simulated indexes added, the On-Demand estimate reaches $30.31/month. The similar write volume keeps the maintenance cost of the two GSIs close despite the large difference in read usage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This produced the clearest ROI comparison in the test.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SimulatedGSI2&lt;/code&gt; served &lt;strong&gt;100 times fewer simulated reads&lt;/strong&gt; than &lt;code&gt;SimulatedGSI1&lt;/code&gt;, but it carried the same write volume and almost the same incremental cost. For this workload, that makes the second GSI the weaker-ROI candidate.&lt;/p&gt;

&lt;p&gt;That does not mean a low-read production index should automatically be removed. It may still support an infrequent but important access pattern. The comparison instead gives a concrete reason to review whether its usage justifies the ongoing cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Adding PITR, backup storage, and Streams reads
&lt;/h2&gt;

&lt;p&gt;I then added three more cost drivers one at a time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Estimate before&lt;/th&gt;
&lt;th&gt;Estimate after&lt;/th&gt;
&lt;th&gt;Increment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enable PITR&lt;/td&gt;
&lt;td&gt;$30.31&lt;/td&gt;
&lt;td&gt;$32.31&lt;/td&gt;
&lt;td&gt;+$2.00/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add 20 GB backup storage&lt;/td&gt;
&lt;td&gt;$32.31&lt;/td&gt;
&lt;td&gt;$34.31&lt;/td&gt;
&lt;td&gt;+$2.00/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add 1,000,000 Stream GetRecords calls over 7 days&lt;/td&gt;
&lt;td&gt;$34.31&lt;/td&gt;
&lt;td&gt;$35.17&lt;/td&gt;
&lt;td&gt;+$0.86/month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Fc92dkc4104lmor0dfm45.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%2Fc92dkc4104lmor0dfm45.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;em&gt;PITR enabled in the simulation. Point-in-time recovery was enabled while the existing GSI assumptions remained unchanged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkcdbtxoxldgurmbk0kh3.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%2Fkcdbtxoxldgurmbk0kh3.png" alt="Tables cost breakdown showing PITR enabled, a Backups plus PITR cost of $2.00, and an On-Demand monthly total of $32.31." width="800" height="448"&gt;&lt;/a&gt;&lt;em&gt;PITR cost result. Enabling PITR increased the simulated On-Demand estimate from $30.31 to $32.31/month, an increment of $2.00/month.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I then kept PITR enabled and added 20 GB of backup storage.&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%2Fj9tx72i0mmceizw61z7z.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%2Fj9tx72i0mmceizw61z7z.png" alt=" " width="800" height="618"&gt;&lt;/a&gt;&lt;em&gt;Backup storage added. Backup storage was increased to 20 GB while PITR and the existing GSI assumptions remained unchanged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqi1aoezsyiyvkoickxnq.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%2Fqi1aoezsyiyvkoickxnq.png" alt="Tables cost breakdown showing 20 GB of backup storage with PITR enabled, a combined Backups plus PITR cost of $4.00, and an On-Demand total of $34.31 per month." width="800" height="450"&gt;&lt;/a&gt;&lt;em&gt;Backup storage cost result. Adding 20 GB of backup storage increased the simulated On-Demand estimate from $32.31 to $34.31/month, another $2.00/month increase.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, I added 1,000,000 Stream GetRecords calls over the same seven-day period. This increased the simulated On-Demand estimate from $34.31 to $35.17/month, an additional $0.86/month.&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%2F6d40ajynxwf4hn10pkrc.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%2F6d40ajynxwf4hn10pkrc.png" alt="Tables Cost Estimation showing one million Stream GetRecords calls over seven days, with two simulated GSIs, PITR enabled, and 20 GB of backup storage." width="799" height="568"&gt;&lt;/a&gt;&lt;em&gt;Streams read activity added. I set the simulation to 1,000,000 DynamoDB Streams GetRecords calls over the 7-day period while keeping the existing GSIs, PITR, and backup assumptions unchanged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dredb2q3bewpf9z4810.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%2F6dredb2q3bewpf9z4810.png" alt="Tables cost breakdown showing one million Stream GetRecords calls and an On-Demand monthly estimate of $35.17 after PITR and 20 GB of backup storage were already included." width="800" height="507"&gt;&lt;/a&gt;&lt;em&gt;Streams cost result. Adding 1,000,000 Stream GetRecords calls increased the simulated On-Demand estimate from $34.31 to $35.17/month, an increment of $0.86/month.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AWS documents PITR and on-demand backup charges as storage-based. DynamoDB Streams reads are billed in Streams read request units, with each &lt;code&gt;GetRecords&lt;/code&gt; call counting as one unit, subject to the applicable AWS pricing and Free Tier conditions.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;$0.86&lt;/code&gt; value above is the &lt;strong&gt;Tables simulator estimate&lt;/strong&gt; for this seven-day Streams input.&lt;/p&gt;

&lt;p&gt;Before adding replicas, the simulated On-Demand total was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$35.17/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Adding two replica Regions
&lt;/h2&gt;

&lt;p&gt;I then added two replica Regions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replica 1: us-east-2
Replica 2: us-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding &lt;code&gt;us-east-2&lt;/code&gt;, the On-Demand estimate increased from &lt;code&gt;$35.17&lt;/code&gt; to &lt;code&gt;$61.97/month&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Increment: +$26.80/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The breakdown showed &lt;code&gt;$24.30/month&lt;/code&gt; for replicated writes and &lt;code&gt;$2.50/month&lt;/code&gt; for replicated storage.&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%2Fai48mb26x67e95jz7bm4.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%2Fai48mb26x67e95jz7bm4.png" alt="Tables Cost Estimation showing us-east-2 added as the first replica Region with two simulated GSIs, PITR enabled, 20 GB of backup storage, and one million Stream GetRecords calls." width="799" height="602"&gt;&lt;/a&gt;&lt;em&gt;First replica Region added. I added us-east-2 as the first simulated replica while keeping the existing GSIs, PITR, backup storage, and Streams assumptions unchanged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhtx8ubts9czadyalhdj8.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%2Fhtx8ubts9czadyalhdj8.png" alt="Tables cost breakdown after adding the us-east-2 replica, showing an On-Demand estimate of $61.97 per month and replica-related write and storage costs." width="800" height="565"&gt;&lt;/a&gt;&lt;em&gt;First replica cost result. Adding us-east-2 increased the simulated On-Demand estimate from $35.17 to $61.97/month, an increment of $26.80/month.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After adding &lt;code&gt;us-west-1&lt;/code&gt;, the estimate increased again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$61.97/month -&amp;gt; $91.79/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second replica added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+$29.82/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With both replicas present, Tables showed &lt;code&gt;$51.32/month&lt;/code&gt; for replicated writes and &lt;code&gt;$5.30/month&lt;/code&gt; for replicated storage. The combined replica increase was therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+$56.62/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F8wi5wuuiqv4500qxjynd.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%2F8wi5wuuiqv4500qxjynd.png" alt="Tables Cost Estimation showing two replica Regions, us-east-2 and us-west-1, with the existing GSIs, PITR, backup storage, and Streams assumptions still configured." width="799" height="616"&gt;&lt;/a&gt;&lt;em&gt;Second replica Region added. I then added us-west-1 as a second simulated replica while keeping the rest of the workload unchanged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dqesqt0f8j7m8kin5h4.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%2F8dqesqt0f8j7m8kin5h4.png" alt="Tables cost breakdown with replica Regions us-east-2 and us-west-1, showing a final On-Demand estimate of $91.79 per month with replicated write and storage costs." width="800" height="562"&gt;&lt;/a&gt;&lt;em&gt;Two-replica cost result. Adding us-west-1 increased the simulated On-Demand estimate from $61.97 to $91.79/month. The two replicas added $56.62/month in total compared with the pre-replica state.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The replica increments differ because the selected Regions use different regional pricing.&lt;/p&gt;

&lt;p&gt;This behavior is consistent with AWS DynamoDB Global Tables billing, where replicated writes are charged in every Region containing a replica and each replica also carries storage cost.&lt;/p&gt;

&lt;p&gt;Tables also showed the one-year and three-year reserved options as &lt;strong&gt;Not offered&lt;/strong&gt; after the Global Table scenario was added. AWS documentation states that reserved capacity cannot be purchased for replicated write capacity units.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical takeaways
&lt;/h2&gt;

&lt;p&gt;The synthetic baseline started at &lt;code&gt;$13.84/month&lt;/code&gt; On-Demand. After adding two GSIs, PITR, backup storage, Streams reads, and two replica Regions, the final simulated total reached &lt;code&gt;$91.79/month&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The component-level comparison was more useful than the final number alone.&lt;/p&gt;

&lt;p&gt;For this workload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GSI write activity contributed much more to index cost than the difference in read activity.&lt;/li&gt;
&lt;li&gt;The lower-read GSI had nearly the same maintenance cost as the higher-read GSI because both used the same simulated write volume.&lt;/li&gt;
&lt;li&gt;PITR, backup storage, and Streams reads produced smaller incremental increases.&lt;/li&gt;
&lt;li&gt;The two replica Regions were the largest incremental cost driver in the simulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These results are specific to the assumptions used in this test. Production traffic, item sizes, table class, Free Tier eligibility, discounts, Region-specific pricing, and the business value of individual access patterns can all change the outcome.&lt;/p&gt;

&lt;p&gt;For me, the useful part of this workflow is being able to compare these design choices before changing the infrastructure. The simulation does not replace real usage data or an AWS bill, but it provides a consistent way to test assumptions and identify which cost drivers deserve closer review.&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>finops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Running, Saving, and Reopening a Parameterized DynamoDB PartiQL SELECT in Tables</title>
      <dc:creator>Sidharth Bangera</dc:creator>
      <pubDate>Thu, 08 Oct 2026 18:02:07 +0000</pubDate>
      <link>https://dev.to/sidharth_bangera/running-saving-and-reopening-a-parameterized-dynamodb-partiql-select-in-tables-48d4</link>
      <guid>https://dev.to/sidharth_bangera/running-saving-and-reopening-a-parameterized-dynamodb-partiql-select-in-tables-48d4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When I reuse a database query, I want the query structure to stay consistent while the input value can change.&lt;/p&gt;

&lt;p&gt;In this walkthrough, I use &lt;a href="https://tables.serverlesscreed.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Tables by ServerlessCreed&lt;/strong&gt;&lt;/a&gt;, a DynamoDB GUI with a PartiQL Workbench, to run a parameterized DynamoDB PartiQL &lt;code&gt;SELECT&lt;/code&gt;, verify the returned items, save the statement, restart the application, reopen the saved query, and verify the result again.&lt;/p&gt;

&lt;p&gt;I also include a simple syntax-error recovery check. The test uses a deterministic fixture, so I know the expected result before execution and can verify the workflow against actual data rather than relying only on a success message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To reproduce the workflow, you need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://tables.serverlesscreed.com/" rel="noopener noreferrer"&gt;Tables by ServerlessCreed&lt;/a&gt; installed. The installer is available from the &lt;a href="https://tables.serverlesscreed.com/downloads" rel="noopener noreferrer"&gt;official download page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Access to a non-production DynamoDB environment.&lt;/li&gt;
&lt;li&gt;A table with predictable test data.&lt;/li&gt;
&lt;li&gt;A known expected result that you can compare with the query output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this test, I used a table named &lt;code&gt;table_bulk&lt;/code&gt; with 120 deterministic items. The fixture was prepared so that 30 items had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status = BULK_EDITED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this table, &lt;code&gt;OrderId&lt;/code&gt; is the partition key and &lt;code&gt;Status&lt;/code&gt; is the sort key. Because &lt;code&gt;BULK_EDITED&lt;/code&gt; appears on multiple items, &lt;code&gt;Status&lt;/code&gt; was a useful field for testing a parameterized query that returned more than one record.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The query I wanted to reuse
&lt;/h2&gt;

&lt;p&gt;Instead of putting the filter value directly into the SQL, I used a named parameter:&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="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"table_bulk"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"Status"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;statusValue&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameter value for this run was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;statusValue = BULK_EDITED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the query structure reusable while letting the value be supplied separately when the statement is run.&lt;/p&gt;

&lt;p&gt;I opened the &lt;strong&gt;PartiQL Workbench&lt;/strong&gt;, selected &lt;code&gt;table_bulk&lt;/code&gt;, and entered the statement. Autocomplete suggestions were available again in the v3.3.37 build while I was typing.&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%2Ffmz0j2mqrxplebnmq0eg.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%2Ffmz0j2mqrxplebnmq0eg.png" alt="Tables PartiQL Workbench showing table_bulk selected and the Status attribute suggested by autocomplete" width="781" height="252"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After selecting &lt;code&gt;table_bulk&lt;/code&gt;, Tables shows the &lt;code&gt;Status&lt;/code&gt; attribute in autocomplete while I build the PartiQL SELECT.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Supplying the parameter and reviewing the query
&lt;/h2&gt;

&lt;p&gt;When I ran the parameterized statement, Tables opened a &lt;strong&gt;Query parameters&lt;/strong&gt; dialog and detected &lt;code&gt;statusValue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I entered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BULK_EDITED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and chose &lt;strong&gt;Load for review&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tables then resolved the parameterized statement to:&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="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"table_bulk"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"Status"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'BULK_EDITED'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This review step was useful because I could see the exact statement that would be executed before running it.&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%2F4gtmh1l9a6shfyc6yae5.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%2F4gtmh1l9a6shfyc6yae5.png" alt="Tables PartiQL Workbench showing the parameterized SELECT before entering the query parameter." width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkd0b3o760rufv57bxxlg.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%2Fkd0b3o760rufv57bxxlg.png" alt="Tables Query parameters dialog prompting for the statusValue input before loading the query for review." width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwqiyr9a3k4aidiypqqko.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%2Fwqiyr9a3k4aidiypqqko.png" alt="Tables Query parameters dialog showing statusValue with the value BULK_EDITED." width="800" height="388"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After running the parameterized statement, Tables prompts for statusValue; I entered BULK_EDITED and loaded the resolved query for review.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Verifying the first result
&lt;/h2&gt;

&lt;p&gt;I then ran the resolved query.&lt;/p&gt;

&lt;p&gt;The expected result was 30 matching items because the fixture had been prepared with exactly 30 items whose &lt;code&gt;Status&lt;/code&gt; value was &lt;code&gt;BULK_EDITED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tables returned &lt;strong&gt;30 items&lt;/strong&gt;, which matched that expectation.&lt;/p&gt;

&lt;p&gt;This was the important verification point for the first run. I was not relying only on a generic success message; I had a known fixture and an expected result before execution.&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%2Fax9jcvugeqq6x9a74qrc.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%2Fax9jcvugeqq6x9a74qrc.png" alt="Tables PartiQL results showing a successful query with 30 returned BULK_EDITED items." width="800" height="514"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After running the resolved SELECT, Tables returns the expected 30 items from the deterministic fixture.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Saving the statement and reopening it after restart
&lt;/h2&gt;

&lt;p&gt;Next, I saved the &lt;strong&gt;parameterized template&lt;/strong&gt; rather than only the resolved literal query.&lt;/p&gt;

&lt;p&gt;The saved statement retained:&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="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"table_bulk"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"Status"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;statusValue&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then restarted Tables, returned to the PartiQL Workbench, and selected the saved statement from &lt;strong&gt;My statements&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The parameterized query itself was still there after restart, but the previous parameter value was not retained. Selecting the saved query opened the &lt;strong&gt;Query parameters&lt;/strong&gt; dialog again, so I entered &lt;code&gt;BULK_EDITED&lt;/code&gt; a second time.&lt;/p&gt;

&lt;p&gt;That behavior is important: Tables preserved the reusable query template, while the input value had to be supplied again when reopening it.&lt;/p&gt;

&lt;p&gt;After loading the value for review and rerunning the query, Tables again returned the expected 30 items.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;First run&lt;/th&gt;
&lt;th&gt;Reopened run&lt;/th&gt;
&lt;th&gt;Expected keys/items&lt;/th&gt;
&lt;th&gt;Actual keys/items&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query template&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{{statusValue}}&lt;/code&gt; present&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{{statusValue}}&lt;/code&gt; present&lt;/td&gt;
&lt;td&gt;Same saved statement&lt;/td&gt;
&lt;td&gt;Same saved statement reopened&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter value&lt;/td&gt;
&lt;td&gt;Entered &lt;code&gt;BULK_EDITED&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Re-entered &lt;code&gt;BULK_EDITED&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Same input value&lt;/td&gt;
&lt;td&gt;Same input value&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;30 items&lt;/td&gt;
&lt;td&gt;30 items&lt;/td&gt;
&lt;td&gt;30 deterministic fixture items matching &lt;code&gt;Status = BULK_EDITED&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Same 30-item result set observed after restart&lt;/td&gt;
&lt;td&gt;None observed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I did not export a separate key manifest for this run, so the comparison was based on the deterministic fixture and the matching 30-item result before and after restart.&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%2F8yqpub0mwr2rh0lypa2l.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%2F8yqpub0mwr2rh0lypa2l.png" alt="Tables PartiQL Workbench showing the saved parameterized statement reopened with a Query parameters dialog for statusValue." width="799" height="481"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After restarting Tables and reopening the saved statement, the parameterized query remains intact and Tables prompts me to enter statusValue again.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Recovering from a syntax error
&lt;/h2&gt;

&lt;p&gt;I also tested a simple recovery case by deliberately changing the statement to invalid syntax:&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;SELECCC&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"table_bulk"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"Status"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'BULK_EDITED'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tables rejected the statement and displayed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Statement wasn't well formed, can't be processed: Expected data manipulation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The malformed statement remained visible in the editor, so I could correct it directly instead of recreating it from scratch.&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%2Fn986ys3so8lg132p1xba.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%2Fn986ys3so8lg132p1xba.png" alt="Tables PartiQL Workbench showing a malformed SELECCC statement and the resulting syntax error." width="800" height="511"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After running the deliberately malformed SELECCC statement, Tables reports a syntax error while leaving the statement in the editor for correction.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I corrected the query and ran it again. Tables reported a successful execution and returned &lt;strong&gt;30 items&lt;/strong&gt;, matching the expected fixture result.&lt;/p&gt;

&lt;p&gt;The query history also kept the failed statement and the successful corrected statement as separate entries during this retest.&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%2Fa05xxd5b33yw7213bagy.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%2Fa05xxd5b33yw7213bagy.png" alt="Tables PartiQL Workbench showing the corrected SELECT and a successful 30-row result after syntax-error recovery." width="800" height="517"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After correcting the statement, Tables executes the SELECT successfully and returns the expected 30 matching items.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;This test confirmed the full workflow I wanted to verify in Tables: creating a parameterized DynamoDB PartiQL query, supplying the parameter value, checking the returned items, saving the query template, reopening it after restart, and running it again with the same expected result.&lt;/p&gt;

&lt;p&gt;The saved statement retained the &lt;code&gt;{{statusValue}}&lt;/code&gt; placeholder, while the parameter value had to be entered again after reopening. With the same &lt;code&gt;BULK_EDITED&lt;/code&gt; input and unchanged fixture, both runs returned the expected 30 items.&lt;/p&gt;

&lt;p&gt;The syntax-error check also showed that a malformed statement remained available for correction, and the corrected query returned the expected result afterward.&lt;/p&gt;

&lt;p&gt;For this test, the most useful part of the workflow was keeping the query reusable without hard-coding the input value into the saved template, while still verifying the result against a deterministic fixture.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
