<?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>Forem: apstndb</title>
    <description>The latest articles on Forem by apstndb (@apstndb).</description>
    <link>https://forem.com/apstndb</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F49074%2F352dc709-9eb7-4642-b6a7-e18ed23196c5.jpeg</url>
      <title>Forem: apstndb</title>
      <link>https://forem.com/apstndb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/apstndb"/>
    <language>en</language>
    <item>
      <title>Is Spanner Really That Expensive? The Surprising Break-Even Point with Firestore</title>
      <dc:creator>apstndb</dc:creator>
      <pubDate>Mon, 14 Jul 2025 02:07:50 +0000</pubDate>
      <link>https://forem.com/apstndb/is-spanner-really-that-expensive-the-surprising-break-even-point-with-firestore-2ma9</link>
      <guid>https://forem.com/apstndb/is-spanner-really-that-expensive-the-surprising-break-even-point-with-firestore-2ma9</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note from the author:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;This article was originally written in Japanese for the community in Japan. The original version can be found &lt;a href="https://zenn.dev/apstndb/articles/spanner-cost-comparison-firestore" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Believing the content could be valuable to a global audience, I've translated it into English. The translation was performed with the assistance of Google's Gemini 2.5 Pro. I have reviewed and edited the translation for accuracy and clarity.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A recent article on the tech blog of &lt;strong&gt;KAUCHE, a Japanese e-commerce company,&lt;/strong&gt; "&lt;a href="https://zenn.dev/kauche/articles/1e733da3748ee1" rel="noopener noreferrer"&gt;Firestore → Cloud Spanner DB Cost Reduction of 93%! The Complete Record of a Year-Long Zero-Downtime Migration&lt;/a&gt;" (in Japanese), has been making waves.&lt;br&gt;
Anyone who has used Firestore (or its predecessor, Datastore) knows that beyond a certain usage level, it can become more expensive than Spanner. However, database migrations are notoriously difficult, so many users likely remain on Firestore despite the cost. The fact that KAUCHE accomplished a zero-downtime migration from Firestore to Spanner is truly impressive.&lt;/p&gt;

&lt;p&gt;Interestingly, looking at the reactions to this article, I noticed a common thread of surprise that migrating from Firestore to Spanner could actually &lt;em&gt;reduce&lt;/em&gt; costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/search?q=https%3A%2F%2Fzenn.dev%2Fkauche%2Farticles%2F1e733da3748ee1&amp;amp;src=typed_query&amp;amp;f=live" rel="noopener noreferrer"&gt;https://x.com/search?q=https%3A%2F%2Fzenn.dev%2Fkauche%2Farticles%2F1e733da3748ee1&amp;amp;src=typed_query&amp;amp;f=live&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why does this surprise exist, when there are options within Google Cloud like Firestore that can become significantly more expensive than Spanner? I believe it's because Firestore offers a generous daily free tier, making it easy to start small, while Spanner has no free tier and is plagued by a persistent preconception that it is "expensive."&lt;/p&gt;

&lt;p&gt;This misconception can hinder optimal technology choices, leading to lost opportunities for developers, businesses, and even Google. It can cause tangible financial harm and create significant technical debt by forcing a difficult database migration in the future.&lt;/p&gt;

&lt;p&gt;Google itself has been trying to dispel this myth, publishing articles like "&lt;a href="https://cloud.google.com/blog/products/databases/cloud-spanner-myths-busted" rel="noopener noreferrer"&gt;Cloud Spanner myths busted&lt;/a&gt;" and holding sessions like the "&lt;a href="https://www.youtube.com/playlist?list=PLIivdWyY5sqJPSoX2R4mRq_wyg0JTjrAG" rel="noopener noreferrer"&gt;Spanner: Database Unlimited Series&lt;/a&gt;" based on it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why did this "expensive" image stick?&lt;/strong&gt;&lt;br&gt;
As Google's article mentions, it's likely due to outdated impressions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High minimum cost for production instances with an SLA:&lt;/strong&gt; It used to require a minimum of 3 nodes, costing around $2,000/month even in the cheapest &lt;code&gt;us-central1&lt;/code&gt; region.

&lt;ul&gt;
&lt;li&gt;In &lt;a href="https://cloud.google.com/spanner/docs/release-notes#September_25_2019" rel="noopener noreferrer"&gt;September 2019&lt;/a&gt;, this was reduced to 1 node with an SLA, cutting the cost by two-thirds.&lt;/li&gt;
&lt;li&gt;With granular instance sizing (&lt;a href="https://cloud.google.com/spanner/docs/release-notes#May_31_2022" rel="noopener noreferrer"&gt;GA in May 2022&lt;/a&gt;), it became possible to get an SLA with just 0.1 nodes (100 PUs), reducing the cost by another factor of 10.&lt;/li&gt;
&lt;li&gt;The cost is now comparable to a Cloud SQL &lt;code&gt;db-g1-small&lt;/code&gt; instance with an HA configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No free development environment:&lt;/strong&gt; Since it's not open-source, development environments incurred similar costs.

&lt;ul&gt;
&lt;li&gt;This has been addressed with the official &lt;a href="https://cloud.google.com/spanner/docs/emulator" rel="noopener noreferrer"&gt;Spanner emulator&lt;/a&gt; (&lt;a href="https://cloud.google.com/spanner/docs/release-notes#July_30_2020" rel="noopener noreferrer"&gt;GA in July 2020&lt;/a&gt;) and &lt;a href="https://cloud.google.com/spanner/docs/free-trial-instance" rel="noopener noreferrer"&gt;free trial instances&lt;/a&gt; (&lt;a href="https://cloud.google.com/spanner/docs/release-notes#September_08_2022" rel="noopener noreferrer"&gt;GA in September 2022&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Despite these efforts, the message may not have fully resonated without concrete numbers. And comparing it to competitor products is often frowned upon.&lt;/p&gt;

&lt;p&gt;So, I thought, why not do a quantitative comparison between Google Cloud's own database offerings? There seems to be value in making that public.&lt;/p&gt;
&lt;h2&gt;
  
  
  Firestore vs. Spanner: A Price Comparison
&lt;/h2&gt;

&lt;p&gt;In this article, I'll perform a price comparison between Firestore and Spanner, inspired by the cost issues highlighted by KAUCHE's experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A few notes on the comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a simplified comparison focusing only on Firestore and Spanner among Google Cloud's database offerings.

&lt;ul&gt;
&lt;li&gt;I'll assume Firestore is used as a backend database. The differences between Native mode and Datastore mode are not critical to the conclusion.&lt;/li&gt;
&lt;li&gt;I will not compare them with services from other cloud providers.&lt;/li&gt;
&lt;li&gt;This is not a general SQL vs. NoSQL or SQL vs. Distributed SQL comparison.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The comparison is based solely on information from the official documentation.

&lt;ul&gt;
&lt;li&gt;No load testing is involved.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Since a key goal is to raise awareness, I will be direct about the cost challenges of Firestore, based on the data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Extracting Relevant Values from Official Docs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All values are as of July 9, 2025.&lt;/li&gt;
&lt;li&gt;The region is standardized to &lt;code&gt;asia-northeast1&lt;/code&gt; (Tokyo).&lt;/li&gt;
&lt;li&gt;Currency is kept in USD to avoid exchange rate fluctuations.&lt;/li&gt;
&lt;li&gt;Committed Use Discounts (CUDs) are omitted as they are the same for both Firestore and Spanner (20% for 1 year, 40% for 3 years) and don't affect the relative comparison.&lt;/li&gt;
&lt;li&gt;Network egress fees are the same for both and are not included in this comparison's scope, though they can affect the total cost.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Firestore
&lt;/h4&gt;

&lt;p&gt;Firestore is entirely pay-as-you-go, so all the pricing information is on one page.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/firestore/pricing?hl=en" rel="noopener noreferrer"&gt;Firestore pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/datastore/pricing?hl=en" rel="noopener noreferrer"&gt;Firestore in Datastore mode pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Native mode and Datastore mode have slightly different terminology but the same pricing structure. I'll use the newer Native mode terms.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Firestore Native mode&lt;/th&gt;
&lt;th&gt;Datastore mode&lt;/th&gt;
&lt;th&gt;Free usage&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Document reads&lt;/td&gt;
&lt;td&gt;Entity reads&lt;/td&gt;
&lt;td&gt;50,000 per day&lt;/td&gt;
&lt;td&gt;$0.038 per 100,000 entities/documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document writes&lt;/td&gt;
&lt;td&gt;Entity writes&lt;/td&gt;
&lt;td&gt;20,000 per day&lt;/td&gt;
&lt;td&gt;$0.115 per 100,000 entities/documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document/TTL deletes&lt;/td&gt;
&lt;td&gt;Entity/TTL deletes&lt;/td&gt;
&lt;td&gt;20,000 per day&lt;/td&gt;
&lt;td&gt;$0.013 per 100,000 entities/documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored data&lt;/td&gt;
&lt;td&gt;Stored data&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;td&gt;$0.115/GiB/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PITR data&lt;/td&gt;
&lt;td&gt;PITR data&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;$0.115/GiB/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup data&lt;/td&gt;
&lt;td&gt;Backup data&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;$0.038/GiB/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore operation&lt;/td&gt;
&lt;td&gt;Restore operation&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;$0.256/GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Small operations&lt;/td&gt;
&lt;td&gt;50,000 per day&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Firestore Native mode doesn't have "small operations." Index entry reads and aggregation queries are counted as one document read per 1,000 entries. Also, the &lt;a href="https://cloud.google.com/firestore/native/docs/editions-overview" rel="noopener noreferrer"&gt;Firestore editions overview&lt;/a&gt; states that the Standard edition uses Hybrid storage (SSD &amp;amp; HDD).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Spanner
&lt;/h4&gt;

&lt;p&gt;Spanner is billed based on provisioned capacity in Processing Units (PUs). We need to look at two pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/spanner/pricing" rel="noopener noreferrer"&gt;Spanner pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/spanner/docs/performance" rel="noopener noreferrer"&gt;Performance overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  Compute Capacity
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Edition&lt;/th&gt;
&lt;th&gt;Cost per node (including all three replicas)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1.17 per hour&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;$1.599 per hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Plus&lt;/td&gt;
&lt;td&gt;$2.223 per hour&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The features in the Standard edition are sufficient for this comparison, so I'll focus on that.&lt;/p&gt;
&lt;h5&gt;
  
  
  Database Storage
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Storage Type&lt;/th&gt;
&lt;th&gt;Cost per GB (including all three replicas)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSD&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.39 per GB per month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDD&lt;/td&gt;
&lt;td&gt;$0.078 per GB per month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I'll focus on SSD storage for this comparison.&lt;/p&gt;
&lt;h5&gt;
  
  
  Estimated Throughput from Docs
&lt;/h5&gt;

&lt;p&gt;While database performance typically requires workload-specific benchmarks, Spanner provides estimated throughput for simple workloads in its documentation. This is a reasonable approximation for a comparison with Firestore, which is primarily used for key-based reads and writes without complex queries.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read guidance is given per region (because reads can be served from any read-write or read-only region), while write guidance is for the entire configuration. Read guidance assumes you're reading single rows of 1KB. Write guidance assumes that you're writing single rows at 1KB of data per row.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the estimated throughput for a regional configuration like &lt;code&gt;asia-northeast1&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/spanner/docs/performance#regional-performance" rel="noopener noreferrer"&gt;Performance for regional configurations&lt;/a&gt;&lt;br&gt;
Each 1,000 processing units (1 node) of compute capacity can provide the following peak performance (at 100% CPU) in a regional instance configuration:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Peak reads (QPS per region)&lt;/th&gt;
&lt;th&gt;Peak writes (QPS total)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SSD: 22,500&lt;/strong&gt; &lt;br&gt; HDD: 1,500&lt;/td&gt;
&lt;td&gt;or &lt;strong&gt;SSD: 3,500&lt;/strong&gt; &lt;br&gt; HDD: 3,500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I'll use the highlighted SSD values.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;(Note)&lt;/strong&gt; This catalog spec has improved significantly. An announcement on &lt;a href="https://cloud.google.com/spanner/docs/release-notes#October_11_2023" rel="noopener noreferrer"&gt;October 11, 2023&lt;/a&gt; detailed &lt;a href="https://cloud.google.com/spanner/docs/performance#improved-performance" rel="noopener noreferrer"&gt;Performance and storage improvements&lt;/a&gt;, boosting typical throughput by 1.5x for both reads and writes and increasing the storage limit by 2.5x. If you want to verify these numbers, you can check the YCSB benchmark results in "&lt;a href="https://cloud.google.com/blog/products/databases/benchmarking-spanner-for-key-value-workloads" rel="noopener noreferrer"&gt;Benchmarking Spanner's cost-performance for key-value workloads&lt;/a&gt;".&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;
  
  
  Spanner's Minimum Configuration: Price and Throughput
&lt;/h5&gt;

&lt;p&gt;A Spanner instance can be created with as little as 100 Processing Units (PUs), which is 0.1 nodes. This configuration is production-ready and comes with an SLA. Here are the adjusted numbers for this minimum setup:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Standard edition cost (100 PU)&lt;/td&gt;
&lt;td&gt;$0.117 per hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak reads (QPS per region)&lt;/td&gt;
&lt;td&gt;2,250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak writes (QPS total)&lt;/td&gt;
&lt;td&gt;350&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is at 100% CPU utilization. However, the official documentation (&lt;a href="https://cloud.google.com/spanner/docs/cpu-utilization#recommended-max" rel="noopener noreferrer"&gt;Alerts for high CPU utilization&lt;/a&gt;) recommends keeping CPU utilization for high-priority tasks below 65% for regional instances. I will factor this into the comparison.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why 65%?&lt;/strong&gt; While not explicitly stated, this is likely to maintain service levels during a zonal failure. A regional Spanner instance has R/W replicas in three zones. If one zone goes down, you lose 1/3 of your capacity. To handle the same load, the remaining 2/3 of capacity (approx. 67%) must suffice. Staying at 65% ensures that a zone failure won't push CPU usage over 100%. This logic also explains the 45% recommendation for dual/multi-region instances, which have replicas in two R/W regions (losing one leaves 50% capacity).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  The Comparison
&lt;/h3&gt;

&lt;p&gt;Let's find the break-even point between the minimum Spanner configuration and Firestore.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'll assume a steady workload of either 1KB reads only or 1KB writes only.&lt;/li&gt;
&lt;li&gt;I'll assume the schema is well-designed to achieve theoretical performance.&lt;/li&gt;
&lt;li&gt;I'll include Firestore's daily free tier.&lt;/li&gt;
&lt;li&gt;I'll factor in Spanner's 65% recommended CPU utilization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The break-even point is where &lt;code&gt;Firestore Cost = Spanner Cost&lt;/code&gt;. We can find the number of daily operations, &lt;code&gt;x&lt;/code&gt;, with the formula: 

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x=CspannerPfirestore+Ffree
x = \frac{C_{\text{spanner}}}{P_{\text{firestore}}} + F_{\text{free}}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;firestore&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;spanner&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;free&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Variables:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;xx&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = Number of operations per day (what we want to find)&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;FfreeF_{\text{free}}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;free&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = Firestore free tier operations (Reads: 50,000, Writes: 20,000)&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;PfirestoreP_{\text{firestore}}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;firestore&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = Firestore price per operation (Read: \$0.00000038/op, Write: \$0.00000115/op)&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;CspannerC_{\text{spanner}}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;spanner&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = Spanner daily cost (100 PU = \$0.117/hr * 24h = \$2.808/day)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;b&gt;To be more precise:&lt;/b&gt; The above formula is a simplification. To be perfectly accurate, you need to account for Firestore's 100,000-operation billing increments. The break-even point &lt;code&gt;x&lt;/code&gt; is more accurately calculated with the following formula:&lt;/p&gt;

&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x=Ffree+⌈Cspanner/Pfirestore100,000⌉×100,000x = F_{\text{free}} + \lceil\frac{C_{\text{spanner}}/P_{\text{firestore}}}{100,000}\rceil \times 100,000
  &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;free&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;⌈&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;spanner&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;/&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;firestore&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;⌉&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;

&lt;p&gt;Here are the results, visualized with graphs. The plots are continuous approximations for simplicity, but the break-even point calculations are precise.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reads
&lt;/h4&gt;

&lt;p&gt;The break-even point for reads is &lt;strong&gt;7,450,000 reads/day&lt;/strong&gt;, which is about &lt;strong&gt;86 reads/sec&lt;/strong&gt;.&lt;br&gt;
This is only &lt;strong&gt;3.8%&lt;/strong&gt; of the peak performance of a 100 PU Spanner instance.&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%2Fmd33be8qyxnrq8k3or4r.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%2Fmd33be8qyxnrq8k3or4r.png" alt="A line graph comparing the daily cost of Firestore versus Spanner for read operations. The x-axis shows daily reads and the y-axis shows daily cost in USD. The Firestore cost line rises steeply, while the Spanner cost line is flat, showing Spanner is much cheaper at high read volumes."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you utilize Spanner up to the recommended 65% of its theoretical throughput, it becomes &lt;strong&gt;17 times cheaper&lt;/strong&gt; than Firestore. This clearly shows how sticking with Firestore at scale can lead to massive cost increases.&lt;/p&gt;

&lt;h4&gt;
  
  
  Writes
&lt;/h4&gt;

&lt;p&gt;The break-even point for writes is &lt;strong&gt;2,520,000 writes/day&lt;/strong&gt;, which is about &lt;strong&gt;29 writes/sec&lt;/strong&gt;.&lt;br&gt;
This is about &lt;strong&gt;8.1%&lt;/strong&gt; of the peak performance of a 100 PU Spanner instance.&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%2Fc82gs8o49mi03skmx6e3.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%2Fc82gs8o49mi03skmx6e3.png" alt="A line graph comparing the daily cost of Firestore versus Spanner for write operations. The x-axis shows daily writes and the y-axis shows daily cost in USD. The Firestore cost line rises steeply, while the Spanner cost line is flat, demonstrating Spanner's cost-effectiveness at high write volumes."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you utilize Spanner up to the recommended 65% of its throughput, it is &lt;strong&gt;8 times cheaper&lt;/strong&gt; than Firestore.&lt;/p&gt;

&lt;h4&gt;
  
  
  Storage
&lt;/h4&gt;

&lt;p&gt;A direct storage cost comparison is difficult because Firestore uses hybrid storage. However, we can get a rough idea. A 100 PU Spanner instance has a storage limit of 1024 GiB.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Unit Price (per GiB per month)&lt;/th&gt;
&lt;th&gt;Monthly Price (for 1024 GiB)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spanner&lt;/td&gt;
&lt;td&gt;$0.39&lt;/td&gt;
&lt;td&gt;$399.36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firestore&lt;/td&gt;
&lt;td&gt;$0.115&lt;/td&gt;
&lt;td&gt;$117.76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ratio&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;339%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diff&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$281.6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While Spanner's storage unit price is higher, the difference is not the dominant factor. In a scenario where you are maxing out the read/write capacity of a 100 PU instance, the daily cost difference for operations is in the tens of dollars, which far outweighs the storage cost difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analysis
&lt;/h3&gt;

&lt;p&gt;The calculations show that while Spanner isn't always cheaper, it becomes significantly more cost-effective than Firestore beyond a certain scale. This isn't surprising. Both databases offer strong consistency and high availability through geographic distribution, making them comparable services. The main differences are the data model and the billing model.&lt;/p&gt;

&lt;p&gt;Generally, provisioned capacity is discounted relative to on-demand, pay-per-operation pricing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In fact, Firestore and Spanner are more than just similar. It has been suggested since Firestore's launch, and later confirmed in Google research papers, that &lt;strong&gt;Firestore is implemented on top of Spanner&lt;/strong&gt;. The architecture of Firestore is detailed in the &lt;strong&gt;"&lt;a href="https://research.google.com/pubs/firestore-the-nosql-serverless-database-for-the-application-developer/" rel="noopener noreferrer"&gt;Firestore: The NoSQL Serverless Database for the Application Developer&lt;/a&gt;"&lt;/strong&gt; paper, and its implementation on Spanner is further described in &lt;strong&gt;"&lt;a href="https://research.google.com/pubs/transparent-migration-of-datastore-to-firestore/" rel="noopener noreferrer"&gt;Transparent Migration of Datastore to Firestore&lt;/a&gt;"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In other words, Firestore is a high-value managed service that abstracts away complexities like query planning and capacity management by offering Spanner's powerful infrastructure on a multi-tenant basis, which provides an easy, free-to-start experience. The price difference at scale can be seen as the cost of this abstraction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  To Anyone Considering Firestore
&lt;/h2&gt;

&lt;p&gt;If you're considering Firestore for your backend, keep these points in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firestore's cost advantage is limited to a relatively narrow range for a horizontally scaling database: from the free tier up to a few million operations per day.&lt;/li&gt;
&lt;li&gt;Be aware that costs will scale with &lt;code&gt;number of users × operations per user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If your service has a fixed number of users (e.g., an internal tool), Firestore is likely a safe choice. Otherwise, always evaluate the possibility that Spanner will become cheaper.&lt;/li&gt;
&lt;li&gt;If your service can justify the monthly cost of a 100 PU Spanner instance (approx. $85/month), choosing Spanner from the start might give you more peace of mind.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;For real-time database use cases where clients connect directly, Firestore is often the right choice. Building the real-time capabilities yourself on top of Spanner would be a massive undertaking. In that case, the comparison should be against services like Firebase Realtime Database.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Firestore is often perceived as a cheap database, suitable for personal and small-scale projects. However, this analysis shows that the cost break-even point with Spanner's minimum configuration (approx. $85/month) is reached at less than 10% of that Spanner instance's capacity.&lt;/p&gt;

&lt;p&gt;Beyond that point, Spanner becomes dramatically cheaper: up to &lt;strong&gt;17 times cheaper for reads&lt;/strong&gt; and &lt;strong&gt;8 times cheaper for writes&lt;/strong&gt;. An $85/month cost is not significantly higher than the minimum cost for other HA database configurations on Google Cloud.&lt;/p&gt;

&lt;p&gt;So, is Spanner still "expensive"? If you hear someone say that, ask them under what conditions, compared to what, and which specific SKU they are talking about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Firestore Enterprise Edition
&lt;/h2&gt;

&lt;p&gt;For those who think Firestore becomes too expensive, you might want to look at the new Firestore Enterprise edition, announced at Cloud Next 2025 and currently in preview.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While it currently only offers MongoDB compatibility, the &lt;a href="https://cloud.google.com/firestore/native/docs/editions-overview" rel="noopener noreferrer"&gt;Firestore editions overview&lt;/a&gt; states that Native mode support is planned for the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;According to the &lt;a href="https://cloud.google.com/firestore/enterprise/pricing" rel="noopener noreferrer"&gt;Firestore Enterprise edition pricing&lt;/a&gt;, it has a pricing structure geared towards larger-scale users compared to the Standard edition.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Standard edition&lt;/th&gt;
&lt;th&gt;Enterprise edition&lt;/th&gt;
&lt;th&gt;Ratio (Enterprise/Standard)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read Units&lt;/td&gt;
&lt;td&gt;$0.038 per 100,000 docs&lt;/td&gt;
&lt;td&gt;$0.0642 per 1,000,000 units&lt;/td&gt;
&lt;td&gt;16.9 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write Units&lt;/td&gt;
&lt;td&gt;$0.115 per 100,000 docs&lt;/td&gt;
&lt;td&gt;$0.3339 per 1,000,000 units&lt;/td&gt;
&lt;td&gt;29 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored Data&lt;/td&gt;
&lt;td&gt;$0.115/GiB/month&lt;/td&gt;
&lt;td&gt;$0.312/GiB/month&lt;/td&gt;
&lt;td&gt;271 %&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note the change in billing unit from 100K to 1M operations. This makes read/write operations cheaper for small documents. However, units are calculated in 4 KiB increments for reads and 1 KiB for writes. This means documents larger than 24KB (for reads) or 4KB (for writes) could become more expensive than in the Standard edition.&lt;/p&gt;

&lt;p&gt;Even with this new pricing, the fundamental trend of becoming more expensive than Spanner at scale seems to persist. It would be ideal if users who start small with Firestore didn't have to regret their choice later. A future where Firestore offers a provisioned capacity model, similar to Spanner, would be a welcome development, combining the ease of getting started with the cost-efficiency of Spanner at scale.&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>spanner</category>
      <category>firestore</category>
      <category>cost</category>
    </item>
    <item>
      <title>A spanner-cli Contributor's Perspective on the Official Spanner CLI</title>
      <dc:creator>apstndb</dc:creator>
      <pubDate>Wed, 25 Jun 2025 08:30:36 +0000</pubDate>
      <link>https://forem.com/apstndb/a-spanner-cli-contributors-perspective-on-the-official-spanner-cli-3aod</link>
      <guid>https://forem.com/apstndb/a-spanner-cli-contributors-perspective-on-the-official-spanner-cli-3aod</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This article is an AI-assisted English translation of my &lt;a href="https://zenn.dev/apstndb/articles/spanner-cli-contributor-perspective" rel="noopener noreferrer"&gt;original Japanese article&lt;/a&gt;. As the author, I wanted to share these insights about the official Spanner CLI release with the international developer community. The translation was performed using Claude to ensure technical accuracy while making the content accessible to a broader audience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The official Spanner CLI has been released in preview.&lt;br&gt;
&lt;a href="https://cloud.google.com/spanner/docs/release-notes" rel="noopener noreferrer"&gt;https://cloud.google.com/spanner/docs/release-notes&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can directly connect and interact with your Spanner database using the Spanner CLI, an interactive shell for Spanner that is built into the Google Cloud CLI. You can use the Spanner CLI to start an interactive session and automate SQL executions from the shell or an input file. This feature is available in &lt;a href="https://cloud.google.com/products#product-launch-stages" rel="noopener noreferrer"&gt;Preview&lt;/a&gt;. For more information, see &lt;a href="https://cloud.google.com/spanner/docs/spanner-cli" rel="noopener noreferrer"&gt;Spanner CLI quickstart&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'll share my observations about what this is, from the perspective of apstndb, an unemployed technology enthusiast who is a contributor to the OSS &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli" rel="noopener noreferrer"&gt;spanner-cli&lt;/a&gt; and the author of its fork &lt;a href="https://github.com/apstndb/spanner-mycli" rel="noopener noreferrer"&gt;spanner-mycli&lt;/a&gt;. For more about spanner-mycli, please read &lt;a href="https://zenn.dev/apstndb/articles/introduce-spanner-mycli" rel="noopener noreferrer"&gt;Why I Forked spanner-cli: Introducing spanner-mycli&lt;/a&gt; (Japanese article).&lt;/p&gt;

&lt;p&gt;Note: This article describes what can be observed about &lt;code&gt;gcloud alpha spanner cli&lt;/code&gt;, which is still in Preview, as of June 25, 2025. Please note that specifications may change at any time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Points of This Article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Spanner CLI is derived from OSS spanner-cli v0.10.6&lt;/li&gt;
&lt;li&gt;Technical evidence discovered through binary analysis&lt;/li&gt;
&lt;li&gt;Unique evolution including introduction of meta-commands&lt;/li&gt;
&lt;li&gt;Expectations for a healthy ecosystem through coexistence of official and OSS versions&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Importance of Interactive Tools
&lt;/h2&gt;

&lt;p&gt;Interactive clients have been and continue to be essential for SQL databases.&lt;/p&gt;

&lt;p&gt;Interactive clients for SQL databases have been recognized as essential tools since the era of &lt;a href="https://dl.acm.org/doi/10.1145/800296.811515" rel="noopener noreferrer"&gt;SEQUEL: A structured English query language&lt;/a&gt;, SQL's predecessor, which made multiple references to interactive systems.&lt;br&gt;
All major RDBMSs without exception provide first-party interactive clients:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL's &lt;a href="https://dev.mysql.com/doc/refman/9.3/en/mysql.html" rel="noopener noreferrer"&gt;mysql&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL's &lt;a href="https://www.postgresql.org/docs/current/app-psql.html" rel="noopener noreferrer"&gt;psql&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Oracle's &lt;a href="https://docs.oracle.com/en/cloud/paas/base-database/cli-reference/index.html" rel="noopener noreferrer"&gt;dbcli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SQL Server's &lt;a href="https://github.com/dbcli/mssql-cli" rel="noopener noreferrer"&gt;mssql-cli&lt;/a&gt;, &lt;a href="https://github.com/microsoft/go-sqlcmd" rel="noopener noreferrer"&gt;sqlcmd&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;SQL Server seems to have a greater emphasis on the GUI tool &lt;a href="https://learn.microsoft.com/en-us/ssms/" rel="noopener noreferrer"&gt;SQL Server Management Studio&lt;/a&gt; compared to others.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How about Spanner? The officially provided interfaces that users could directly use were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/spanner/docs/getting-started/gcloud" rel="noopener noreferrer"&gt;&lt;code&gt;gcloud spanner&lt;/code&gt; subcommand within gcloud CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/spanner/docs/manage-data-using-console" rel="noopener noreferrer"&gt;Spanner Studio&lt;/a&gt; within Cloud Console, Google Cloud's official Web UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gcloud spanner subcommand is not an interactive tool. It's a command that can execute SQL one-off from Bash or PowerShell, or be incorporated into scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud spanner databases execute-sql example-db \
    --sql='SELECT SingerId, AlbumId, AlbumTitle FROM Albums'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, Spanner Studio is operated from a browser and doesn't run standalone locally.&lt;br&gt;
The execution plan display is not suitable for sharing and cannot be used for automation.&lt;/p&gt;

&lt;p&gt;Thus, Spanner lacked what other RDBMSs naturally provided.&lt;br&gt;
Therefore, after &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli" rel="noopener noreferrer"&gt;spanner-cli&lt;/a&gt; developed by current Googler Yuki Furuyama was donated to the &lt;a href="https://github.com/cloudspannerecosystem" rel="noopener noreferrer"&gt;Cloud Spanner Ecosystem organization&lt;/a&gt;, it became the de facto standard tool for Spanner.&lt;/p&gt;

&lt;p&gt;However, since spanner-cli was not part of Google's product, there was a disconnect between Spanner Studio used in official documentation and spanner-cli used primarily by the user community, without official documentation references.&lt;/p&gt;
&lt;h2&gt;
  
  
  Official Spanner CLI
&lt;/h2&gt;

&lt;p&gt;After years of this situation, the official Spanner CLI was suddenly released. This happened on the morning of June 25, 2025 (JST) as I write this article.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;As of June 25, 2025, the &lt;a href="https://cloud.google.com/spanner/docs/spanner-cli" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; states that running &lt;code&gt;gcloud alpha spanner cli&lt;/code&gt; will automatically install it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Spanner CLI is available in the gcloud CLI. When you run the &lt;code&gt;gcloud alpha spanner cli&lt;/code&gt; command for the first time, gcloud CLI automatically installs the Spanner CLI component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, you currently cannot use the official Spanner CLI as described.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud alpha spanner cli --project ${SPANNER_PROJECT_ID} --instance ${SPANNER_INSTANCE_ID} ${SPANNER_DATABASE_ID}  
Pausing command execution:

This command requires the `spannercli` component to be installed. Would you like to install the `spannercli` component to continue command execution? (Y/n)?  y

ERROR: (gcloud.alpha.spanner.cli) The following components are unknown [spannercli].
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the component it's trying to install called &lt;code&gt;spannercli&lt;/code&gt; doesn't exist. What actually exists is &lt;code&gt;spanner-cli&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;$ gcloud components list | grep spanner

Your current Google Cloud CLI version is: 528.0.0
The latest available version is: 528.0.0

To install or remove components at your current SDK version [528.0.0], run:
  $ gcloud components install COMPONENT_ID
  $ gcloud components remove COMPONENT_ID

To update your SDK installation to the latest version [528.0.0], run:
  $ gcloud components update

│ Not Installed │ Spanner Cli                                          │ spanner-cli                  │  12.1 MiB │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you need to install the &lt;code&gt;spanner-cli&lt;/code&gt; component as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud components install --quiet spanner-cli

Your current Google Cloud CLI version is: 528.0.0
Installing components from version: 528.0.0

┌──────────────────────────────────────────────────────┐
│         These components will be installed.          │
├─────────────────────────────────┬─────────┬──────────┤
│               Name              │ Version │   Size   │
├─────────────────────────────────┼─────────┼──────────┤
│ Spanner Cli (Platform Specific) │   1.0.0 │ 12.1 MiB │
└─────────────────────────────────┴─────────┴──────────┘

For the latest full release notes, please visit:
  https://cloud.google.com/sdk/release_notes

Performing in place update...

╔════════════════════════════════════════════════════════════╗
╠═ Downloading: Spanner Cli                                 ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Downloading: Spanner Cli (Platform Specific)             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Spanner Cli                                  ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Spanner Cli (Platform Specific)              ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.                                                                                                                                                                                                                                                                            

Google Cloud CLI works best with Python 3.12 and certain modules.

Setting up virtual environment
Updating modules...
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.5/5.5 MB 31.2 MB/s eta 0:00:00
Modules updated.
Virtual env enabled.

Update done!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, you can use &lt;code&gt;gcloud alpha spanner cli&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;$ gcloud alpha spanner cli --project ${SPANNER_PROJECT_ID} --instance ${SPANNER_INSTANCE_ID} ${SPANNER_DATABASE_ID}
Welcome to Spanner-Cli Client.

Type 'help;' or '\h' for help.
Type 'exit;' or 'quit;' or '\q' to exit.

spanner-cli&amp;gt; DESCRIBE SELECT 1;
+-------------+-------------+
| Column_Name | Column_Type |
+-------------+-------------+
|             | INT64       |
+-------------+-------------+
1 rows in set (0.17 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official Spanner CLI can also be used directly without going through the &lt;code&gt;gcloud&lt;/code&gt; command, as the &lt;code&gt;google-cloud-sdk/bin/spannercli&lt;/code&gt; binary is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ~/google-cloud-sdk/bin/spannercli sql --help
Starts a SQL shell for a Spanner instance.

Usage:
  spanner sql --database=&amp;lt;database&amp;gt; [flags]

Examples:

# Start an interactive SQL shell for the 'my-database' database:
$ spanner sql --database my-database

# Execute a batch of SQL statements from the 'my-commands.sql' file:
$ spanner sql --database my-database --file my-commands.sql

# Execute a batch of SQL statements directly on the command line:
$ spanner sql --database my-database --execute "SELECT * FROM my-table; INSERT INTO my-table VALUES (1, 'apple');"

# SQL statements can be piped into the command:
$ spanner sql --database my-database &amp;lt; my-commands.sql
$ cat my-commands.sql | spanner sql --database my-database



Flags:
      --database string                The name of the database to start the sqlshell for. This flag is required.
      --delimiter string               The delimiter to use in the SQL shell.
      --deployment_endpoint string     The endpoint to use for the Spanner instance.
      --execute string                 The SQL commands to execute. This flag is mutually exclusive with the --source flag.
  -h, --help                           help for sql
      --history string                 The file to use for the SQL shell history.
      --html                           Show output in HTML format.
      --idle_transaction_timeout int   Idle transaction timeout. (default 60)
      --init-command string            The SQL command to execute before the SQL commands in the source file.
      --init-command-add string        The SQL command to execute after the SQL commands in the source file.
      --instance string                The instance ID of the Spanner instance.
      --project string                 The project ID of the Spanner instance.
      --prompt string                  The prompt to use in the SQL shell.
      --role string                    Role to use to connect to the Spanner instance.
      --skip-column-names              Show column names in the SQL shell.
      --skip-system-command            Allow system command in the SQL shell.
      --source string                  The file containing the SQL commands to execute. This flag is mutually exclusive with the --execute flag.
      --table                          Show output in Table format. (default true)
      --tee string                     The file to tee the SQL commands to.
      --xml                            Show output in XML format.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Evidence that Official Spanner CLI is a Derivative of OSS spanner-cli
&lt;/h3&gt;

&lt;p&gt;The relationship between OSS spanner-cli and official Spanner CLI is not mentioned at all in the official &lt;a href="https://cloud.google.com/spanner/docs/spanner-cli" rel="noopener noreferrer"&gt;Spanner CLI documentation&lt;/a&gt;.&lt;br&gt;
However, I can say with confidence: The official Spanner CLI is a derivative of OSS spanner-cli.&lt;/p&gt;

&lt;p&gt;Let me show you the evidence. Let's extract strings contained in the official Spanner CLI binary using the &lt;code&gt;strings&lt;/code&gt; command and filter for a specific string (&lt;code&gt;apstndb&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;$ strings google-cloud-sdk/bin/spannercli | grep apstndb
Bgoogle3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*InputStatement).StripComments
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.SeparateInputString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.SeparateInput
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.newSeparator
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.SeparateInputPreserveComments
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeRawString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeBytesString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeRawBytesString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeStringContent
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.hasStringPrefix
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.hasPrefix
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeStringDelimiter
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).skipComments
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).separate
type:.eq.google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.InputStatement
third_party/golang/github_com/apstndb/gsqlsep/v/v0/separator.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We found &lt;a href="https://github.com/apstndb/gsqlsep" rel="noopener noreferrer"&gt;&lt;code&gt;github.com/apstndb/gsqlsep&lt;/code&gt;&lt;/a&gt;, a third-party library vendored within Google's monorepo &lt;code&gt;google3&lt;/code&gt;.&lt;br&gt;
To tell the truth, this &lt;code&gt;apstndb/gsqlsep&lt;/code&gt; is something I created by improving and turning into a library the statement splitting implementation that spanner-cli had, in order to fix comment handling bugs in spanner-cli with &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/pull/150" rel="noopener noreferrer"&gt;fix comment handling #150&lt;/a&gt;.&lt;br&gt;
No software other than spanner-cli uses this library. This is highly reliable evidence that it's a derivative of spanner-cli.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/github.com/apstndb/gsqlsep?tab=importedby" rel="noopener noreferrer"&gt;https://pkg.go.dev/github.com/apstndb/gsqlsep?tab=importedby&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.amazonaws.com%2Fuploads%2Farticles%2Fbxogrd7fx766jewbkuf6.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%2Fbxogrd7fx766jewbkuf6.png" alt="" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Note
&lt;/h4&gt;

&lt;p&gt;Currently, it's explicitly stated that the official Spanner CLI doesn't support Spanner PostgreSQL interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PostgreSQL interface note&lt;/strong&gt;: For PostgreSQL-dialect databases, you can use the &lt;a href="https://cloud.google.com/spanner/docs/psql-commands" rel="noopener noreferrer"&gt;psql command-line tool&lt;/a&gt;. The examples in this document are intended for GoogleSQL-dialect databases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since &lt;code&gt;apstndb/gsqlsep&lt;/code&gt; is an implementation based on GoogleSQL lexical structure, it cannot support Spanner PostgreSQL interface.&lt;br&gt;
As Spanner-specific features that cannot be supported through PGAdapter are expected to continue increasing, I predict that at some point they will abandon the dependency on &lt;code&gt;apstndb/gsqlsep&lt;/code&gt; and natively support both GoogleSQL/PostgreSQL dialects.&lt;/p&gt;
&lt;h4&gt;
  
  
  Note end
&lt;/h4&gt;

&lt;p&gt;As for when exactly it was forked, since &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/pull/191" rel="noopener noreferrer"&gt;&lt;code&gt;DESCRIBE&lt;/code&gt; that I implemented by October 2024&lt;/a&gt; is available, we can narrow it down to being derived from &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/releases/tag/v0.10.6" rel="noopener noreferrer"&gt;v0.10.6&lt;/a&gt; or later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spanner-cli&amp;gt; DESCRIBE SELECT * FROM Singers;
+-------------+-------------+
| Column_Name | Column_Type |
+-------------+-------------+
| SingerId    | INT64       |
| FirstName   | STRING      |
| LastName    | STRING      |
| SingerInfo  | BYTES       |
| BirthDate   | DATE        |
+-------------+-------------+
5 rows in set (0.17 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, since &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/pull/206" rel="noopener noreferrer"&gt;&lt;code&gt;BEGIN RW ISOLATION LEVEL&lt;/code&gt;&lt;/a&gt; doesn't work, it's highly likely to be before &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/releases/tag/v0.11.0" rel="noopener noreferrer"&gt;v0.11.0&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spanner-cli&amp;gt; BEGIN RW ISOLATION LEVEL SERIALIZABLE;
ERROR: invalid statement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: While many spanner-cli features including &lt;code&gt;DESCRIBE&lt;/code&gt; can still be used in the official Spanner CLI, they may be removed in future releases as they are not documented in the official documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Organizing the Fork Relationships
&lt;/h3&gt;

&lt;p&gt;Based on the investigation so far, the relationships between OSS spanner-cli, official Spanner CLI, and spanner-mycli can be organized as follows (as of June 25, 2025):&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%2F7zb5squ6caq4412trsaa.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%2F7zb5squ6caq4412trsaa.png" alt="Both of official Spanner CLI and spanner-mycli are forked from spanner-cli v0.10.6" width="705" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, both official Spanner CLI and spanner-mycli were forked from spanner-cli v0.10.6, each evolving independently. As of June 25, 2025, OSS spanner-cli has progressed to v0.11.0, and spanner-mycli to v0.19.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Features Added in Official Spanner CLI
&lt;/h2&gt;

&lt;p&gt;The official Spanner CLI has significant changes from OSS spanner-cli even in this initial release.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meta-commands
&lt;/h3&gt;

&lt;p&gt;The biggest change is the introduction of meta-commands.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Displays help information. Same as &lt;code&gt;\h&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delimiter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets the statement delimiter. The default delimiter is a semi-colon.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exits the Spanner CLI. Same as quit.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sends and runs SQL statement in Spanner.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Help&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Displays help information. Same as &lt;code&gt;\?&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notee&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turns off writing to the output file set by the &lt;code&gt;\T&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changes your prompt to a user prompt string.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quits Spanner CLI. Same as exit.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes SQL from an input file. Takes &lt;code&gt;[filename]&lt;/code&gt; as an argument.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes a system shell command.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tee&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Appends command output to a specified &lt;code&gt;[filename]&lt;/code&gt; along with the standard output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connects to another database. Takes the new database name as an argument.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This seems to be strongly influenced by &lt;a href="https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMANDS" rel="noopener noreferrer"&gt;PostgreSQL psql command's meta-commands&lt;/a&gt;.&lt;br&gt;
Client-side features of Spanner CLI will likely be extended as meta-commands in the future.&lt;/p&gt;

&lt;p&gt;Note: Features for interacting with Spanner that are not limited to the client side are still largely undocumented. My intuition is that since it's undesirable for Google's official tools to have inconsistent command systems, it will converge to the &lt;a href="https://cloud.google.com/spanner/docs/jdbc-session-mgmt-commands" rel="noopener noreferrer"&gt;Spanner JDBC&lt;/a&gt; command system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changes in Command-line Options
&lt;/h3&gt;

&lt;p&gt;Command-line options have also changed significantly. Comparing the initial release v1.0.0 of official Spanner CLI with the latest release &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/releases/tag/v0.11.1" rel="noopener noreferrer"&gt;v0.11.1&lt;/a&gt; of OSS spanner-cli, the following differences appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gcloud spanner&lt;/code&gt; integration

&lt;ul&gt;
&lt;li&gt;Connection specification is now common with &lt;code&gt;gcloud spanner&lt;/code&gt;, supporting &lt;code&gt;gcloud config set&lt;/code&gt; etc.&lt;/li&gt;
&lt;li&gt;Authentication: &lt;code&gt;--credential&lt;/code&gt; is removed and integrated into gcloud credential&lt;/li&gt;
&lt;li&gt;However, I confirmed it doesn't work after &lt;code&gt;gcloud auth application-default revoke&lt;/code&gt;, so it seems &lt;code&gt;gcloud auth login&lt;/code&gt; alone isn't sufficient yet
&amp;gt; &lt;code&gt;Error: failed to create sql shell: credentials: could not find default credentials. See https://cloud.google.com/docs/authentication/external/set-up-adc for more information&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GCLOUD_WIDE_FLAG&lt;/code&gt; support - mentions support for other &lt;code&gt;gcloud&lt;/code&gt; level flags like &lt;code&gt;--impersonate-service-account&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Single-letter short forms are removed except for &lt;code&gt;-h&lt;/code&gt; corresponding to &lt;code&gt;--help&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;--verbose&lt;/code&gt;(&lt;code&gt;-v&lt;/code&gt;), which was almost default in spanner-cli, is removed&lt;/li&gt;

&lt;li&gt;Changed from &lt;code&gt;--file&lt;/code&gt; to &lt;code&gt;--source&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Connection API endpoint changes: from &lt;code&gt;--endpoint&lt;/code&gt; to &lt;code&gt;--deployment_endpoint&lt;/code&gt;, &lt;code&gt;--host&lt;/code&gt;, &lt;code&gt;--post&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Output format: Added &lt;code&gt;--html&lt;/code&gt;, &lt;code&gt;--xml&lt;/code&gt; in addition to &lt;code&gt;--table&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Other options removed from OSS spanner-cli: &lt;code&gt;--priority&lt;/code&gt;, &lt;code&gt;--directed-read&lt;/code&gt;, &lt;code&gt;--proto-descriptor-file&lt;/code&gt;, &lt;code&gt;--skip-tls-verify&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Other options added in official Spanner CLI: &lt;code&gt;--init-command string&lt;/code&gt;, &lt;code&gt;--init-command-add string&lt;/code&gt; &lt;code&gt;--tee string&lt;/code&gt;, &lt;code&gt;--idle-transaction-timeout int&lt;/code&gt;, &lt;code&gt;--skip-column-names string&lt;/code&gt;, &lt;code&gt;--skip-system-command string&lt;/code&gt;, &lt;code&gt;--system-command string&lt;/code&gt;, &lt;code&gt;--delimiter string&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;While functionality as a CLI tool is enhanced, some Spanner API-related parts seem to have been removed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Note
&lt;/h4&gt;

&lt;p&gt;Regarding command-line options, there seem to be differences in official Spanner CLI between launching as &lt;code&gt;gcloud alpha spanner cli&lt;/code&gt; and as &lt;code&gt;spannercli sql&lt;/code&gt;.&lt;br&gt;
This is probably because &lt;code&gt;gcloud alpha spanner cli&lt;/code&gt; as a wrapper adjusts for consistency with the &lt;code&gt;gcloud spanner&lt;/code&gt; command. There's a possibility that &lt;code&gt;spannercli sql&lt;/code&gt; may become unavailable in the future.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;gcloud alpha spanner cli&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;spannercli sql&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database ID&lt;/td&gt;
&lt;td&gt;Positional parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--database&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role specification&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--database-role&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--role&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Thoughts as a Spanner Interactive Tool Developer
&lt;/h2&gt;

&lt;p&gt;The birth of official Spanner CLI is very welcome.&lt;br&gt;
This is because, as official documentation is written using it going forward, both officials and the user community will use the same tool and be able to discuss operations and results in a shareable text format.&lt;br&gt;
While &lt;code&gt;EXPLAIN&lt;/code&gt; and &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; are not currently mentioned, I hope we'll be able to discuss execution plans using this tool as well.&lt;/p&gt;

&lt;p&gt;My concern is that unlike OSS-developed spanner-cli, the official Spanner CLI's source is not public.&lt;br&gt;
Being provided only as a compiled binary in the compiled language Go means it's impossible to investigate problems independently and apply patches, which may lead to continued inconvenience for users.&lt;/p&gt;

&lt;p&gt;I think it would be preferable to become like &lt;a href="https://github.com/GoogleCloudPlatform/gsutil" rel="noopener noreferrer"&gt;gsutil&lt;/a&gt;, which is distributed by Google as one of the &lt;code&gt;gcloud components&lt;/code&gt; but developed on GitHub.&lt;/p&gt;

&lt;p&gt;With this, the official tool's future evolution is promised, but I intend to continue developing my spanner-mycli as the only actively developed OSS Spanner interactive client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;When I confirmed the relationship between this official Spanner CLI and OSS spanner-cli, I learned that they are considering putting spanner-cli into maintenance mode and archiving it after the official Spanner CLI goes GA.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/issues/214" rel="noopener noreferrer"&gt;https://github.com/cloudspannerecosystem/spanner-cli/issues/214&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Today Google officially released the official Spanner CLI and it's basically equivalent (or superset) of OSS spanner-cli in terms of functionality.&lt;br&gt;
To prevent confusion from having two separate CLIs, I'm considering to make OSS spanner-cli maintenance mode and archive this repository once the official Spanner CLI goes to GA.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I deeply thank Yuki Furuyama, the author and owner of spanner-cli, who has supported the Spanner community as an interactive client not provided by first-party Google itself for over six and a half years since &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli" rel="noopener noreferrer"&gt;v0.1.0&lt;/a&gt; in October 2018.&lt;/p&gt;

&lt;h2&gt;
  
  
  (2025-06-26) Addendum
&lt;/h2&gt;

&lt;p&gt;As described in spanner-cli's &lt;a href="https://github.com/cloudspannerecosystem/spanner-cli/blob/feb248b968ee0d84226f8f133872929291ad785c/CONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt;, all contributions to projects under the Cloud Spanner Ecosystem organization are made under Google's &lt;a href="https://cla.developers.google.com/" rel="noopener noreferrer"&gt;Contributor License Agreement (CLA)&lt;/a&gt;.&lt;br&gt;
This means contributors have agreed to allow Google to freely use contributions to spanner-cli, so there are no licensing issues with it becoming closed source.&lt;/p&gt;

&lt;p&gt;However, I think not explaining the relationship with spanner-cli, which is familiar to the Spanner community, will cause confusion. I hope this will be explained when it's announced in the official Google Cloud blog at GA.&lt;/p&gt;

&lt;p&gt;Google's decision to maintain an interactive CLI as a first-party tool is definitely a good thing, and I hope it continues to evolve in a positive direction.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>spanner</category>
      <category>database</category>
      <category>abotwrotethis</category>
    </item>
  </channel>
</rss>
