<?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: Greg Goodman</title>
    <description>The latest articles on DEV Community by Greg Goodman (@gregcrl).</description>
    <link>https://dev.to/gregcrl</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%2F689757%2F773511b0-49e7-4f48-bc12-43db42c0cb8d.png</url>
      <title>DEV Community: Greg Goodman</title>
      <link>https://dev.to/gregcrl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gregcrl"/>
    <language>en</language>
    <item>
      <title>Elastic Migration of a Multi-Region CockroachDB Cluster</title>
      <dc:creator>Greg Goodman</dc:creator>
      <pubDate>Mon, 23 Jan 2023 13:40:39 +0000</pubDate>
      <link>https://dev.to/gregcrl/elastic-migration-of-a-multi-region-cockroachdb-cluster-557k</link>
      <guid>https://dev.to/gregcrl/elastic-migration-of-a-multi-region-cockroachdb-cluster-557k</guid>
      <description>&lt;p&gt;In this blog post, my colleague Drew Deally and I are going to work through the exercise of migrating a MultiRegion CockroachDB cluster from one set of cloud Regions to another using the elastic nature of a CockroachDB cluster.  That is, we’re going to move the cluster to a whole new set of nodes by expanding the cluster (adding nodes during runtime) and then contracting it (removing nodes from the live cluster).  And we are going to do it without suffering any period of data unavailability or even an appreciable drop in cluster performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  In the Last Episode
&lt;/h2&gt;

&lt;p&gt;This is a follow-up to a &lt;a href="https://andrewdeally.medium.com/migrating-a-cluster-using-cockroachdb-elastic-architecture-14f5c7147746" rel="noopener noreferrer"&gt;previous blog post&lt;/a&gt;, in which Drew and I worked through the same exercise - migrating a CockroachDB cluster from one set of cloud Regions to another - but with a cluster that did not make use of CockroachDB’s MultiRegion SQL abstractions.  We encourage you to read that post, if you haven’t already.&lt;/p&gt;

&lt;p&gt;To summarize that exercise, we used the elastic property of a CockroachDB cluster to &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add nodes in the regions we want to the cluster to occupy&lt;/li&gt;
&lt;li&gt;decommission nodes in the regions we want to vacate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In between these two steps, we used zone configurations to apply replica placement constraints to our databases, forcing our application data out of the regions we’re evacuating.  When we decommissioned the nodes in the evacuated regions, we did it one node at a time, allowing the nodes to shed any remaining replicas (of system ranges) they were hosting.&lt;/p&gt;

&lt;p&gt;It’s worth noting that we didn’t have to push the application data off the retired nodes before decommissioning them.  Decommissioning a node causes it to migrate its replicas away to other nodes before shutting down.  Since a node in the process of being decommissioned is ineligible to receive new replicas, it’s possible to decommission multiple nodes at once, and let the cluster move their replicas en masse to eligible nodes.  We moved the application data in a separate step for two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We wanted things to happen one operation at a time, so we could monitor the progress of each step.&lt;/li&gt;
&lt;li&gt;We wanted to be able to roll back the migration if necessary, an option that’s often required by customers’ IT departments to minimize risk.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why a MultiRegion cluster is different
&lt;/h2&gt;

&lt;p&gt;The procedure we outlined in the last article works for a CockroachDB cluster with databases that do NOT use the &lt;a href="https://www.cockroachlabs.com/docs/v22.2/multiregion-overview" rel="noopener noreferrer"&gt;MultiRegion capabilities&lt;/a&gt; introduced in version 21.1, but it’s not quite the right mechanism for a MultiRegion database. Why not?  &lt;/p&gt;

&lt;p&gt;A MultiRegion database is assigned to occupy specific Database Regions, including one Primary and zero or more additional Regions.  Those assignments will have to be adjusted to match the changing network topology.  Changing the Database Regions will have much the same effect as applying the replica placement constraints using zone configurations as in the previous example; it will force replicas to move out of Regions they’re not allowed to occupy and into Regions where they are allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elastic Migration of a MultiRegion Database
&lt;/h2&gt;

&lt;p&gt;We’re going to repeat the exercise from the previous article, but with a difference.  We’ll migrate a CockroachDB cluster from one set of nodes to another by adding new nodes to the cluster and then retiring the old ones.  But this time, the cluster hosts a MultiRegion database, so we’ll use the SQL MultiRegion commands to relocate the database to the new regions before retiring the old ones.&lt;/p&gt;

&lt;p&gt;As before, the process outlined here allows the migration to take place without database downtime, without changing the cluster’s identity, and without relying on any tools outside of CockroachDB’s native functionality, or even on any of CockroachDB Enterprise features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting and Ending States
&lt;/h2&gt;

&lt;p&gt;In our example, we start with a CockroachDB cluster deployed across 3 regions: &lt;strong&gt;us-east1&lt;/strong&gt;, &lt;strong&gt;us-east2&lt;/strong&gt; and &lt;strong&gt;us-central1&lt;/strong&gt;, with &lt;strong&gt;us-central1&lt;/strong&gt; serving as the Primary Region.  The desired end result is to have the cluster occupy a completely different (non-overlapping) set of regions: &lt;strong&gt;us-east4&lt;/strong&gt;, &lt;strong&gt;us-west4&lt;/strong&gt;, &lt;strong&gt;us-west3&lt;/strong&gt;, with &lt;strong&gt;us-east4&lt;/strong&gt; serving as Primary Region.&lt;/p&gt;

&lt;p&gt;You may have noticed another small difference between this exercise and the one we undertook in the last article: We’re not keeping any of the original regions in the final cluster. Because our Primary Region will be going away, we'll have to change the Primary Region for the database(s) in the cluster.  &lt;/p&gt;

&lt;p&gt;This chart from the DB Console illustrates the current distribution of replicas across Regions in our cluster:&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%2F0zm4qozvgcmw7o8xbto6.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%2F0zm4qozvgcmw7o8xbto6.png" alt="Initial Replica Distribution" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this one represents the desired state this exercise will produce:&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%2F6zxv2vjlxmmot1j5itnn.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%2F6zxv2vjlxmmot1j5itnn.png" alt="Final Replica Distribution" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Note: The higher number of replicas in the final distribution of the TPCC database is due to changing the Replication Factor from 3 to 5, a side effect of making our database Multi-Region.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of the Process
&lt;/h2&gt;

&lt;p&gt;As illustrated above, we’re starting with 9 nodes in 3 regions.  We’ll add another 9 nodes in 3 more regions, for a total of 18 nodes.  Then we’ll manage the transfer of data out of the 3 regions we want to vacate, and into the 3 regions we’ve added.  When we retire the 9 nodes in the vacated regions, we’ll leave the cluster with a new set of 9 nodes in a new set of 3 regions.&lt;/p&gt;

&lt;p&gt;Here’s a high-level view of the steps we’re going to take.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify all schema changes and upgrades have been finalized and backup the cluster &lt;/li&gt;
&lt;li&gt;Prepare all the new nodes with appropriate Cockroachdb binaries &lt;/li&gt;
&lt;li&gt;Update cluster certificates&lt;/li&gt;
&lt;li&gt;Add new nodes to the cluster&lt;/li&gt;
&lt;li&gt;(Optional) Modify cluster configuration to speed up range migration&lt;/li&gt;
&lt;li&gt;Add new Regions to the database(s)&lt;/li&gt;
&lt;li&gt;Change the database Region assignments, and monitor the migration of replicas to the new nodes&lt;/li&gt;
&lt;li&gt;Manually fail the application over to the new primary region&lt;/li&gt;
&lt;li&gt;Decommission nodes to be retired, one at a time&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;We created a cluster of 9 nodes, and used the &lt;code&gt;cockroach workload ... tpcc&lt;/code&gt; command to initialize and run the TPCC workload on the cluster.  We can see on the DB Console that our workload is roughly generating 3379 queries per second with a p99 Latency of 15 ms, which is expected under normal circumstances:  &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%2F1c821rbemzjozhgb4jiv.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%2F1c821rbemzjozhgb4jiv.png" alt="Cluster Performance Summary" width="800" height="886"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that our TPCC database was created as a cluster-spanning database, not a MultiRegion database.  We’ll turn it into a MultiRegion database now, by assigning it a Primary Region, and adding the other regions that can host its data.&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%2Fxyfe62uc1oxv2eism8ms.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%2Fxyfe62uc1oxv2eism8ms.png" alt="Make the database Multi-Region" width="800" height="1023"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see the new database zone configuration (automatically adjusted as a result of the MultiRegion commands we executed) that distributes the replicas across the 3 regions and places 2 voting replicas, including the leaseholder, in the primary region.&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%2Fmy16p7wys98uase9vzjs.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%2Fmy16p7wys98uase9vzjs.png" alt="Initial Mult-Region Zone Configuration" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over the course of about 70 minutes, the database moves the replicas to their designated locations.  This is performed in the background, and doesn’t interfere with the system’s ability to service application requests.&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%2F4jaenfunl23so4m1qsfc.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%2F4jaenfunl23so4m1qsfc.png" alt="Replicas Relocate" width="800" height="477"&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.amazonaws.com%2Fuploads%2Farticles%2Ff16gmy4rngtqjya9u2si.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%2Ff16gmy4rngtqjya9u2si.png" alt="Uninterrupted SQL Performance" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ve completed step 1 of the process we laid out above; our initial cluster and MultiRegion database are now set up, and we can start our migration.&lt;/p&gt;

&lt;p&gt;We’ll gloss over steps 2-4, and assume that we’ve successfully added 9 more nodes in 3 new regions to the cluster:&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%2Fdafqzimc8n1p5e3dsvj1.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%2Fdafqzimc8n1p5e3dsvj1.png" alt="Expanded Cluster" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, we can see that our TPCC database still occupies only the regions designated for it, while the system ranges are distributed across the whole cluster.&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%2F92n8lptosr4cvhvckovl.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%2F92n8lptosr4cvhvckovl.png" alt="Replica Distribution" width="800" height="425"&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.amazonaws.com%2Fuploads%2Farticles%2F1exafr716dlxrildnwac.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%2F1exafr716dlxrildnwac.png" alt="Replica Distribution (expanded)" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is another minor difference from our previous exercise.  In that case, we used a zone configuration with replica placement constraints to keep the system from taking advantage of the new nodes and distributing our database replicas more widely.  (Recall that CockroachDB’s default policy is to spread replicas geographically, both to balance load and to protect against localized failures).  We didn’t do that this time because our MultiRegion database is already constrained to occupy only specific regions, and cannot currently place replicas in the regions where our new nodes are located.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing the Database Regions
&lt;/h2&gt;

&lt;p&gt;The next steps are (#6) to add the new regions to the database, (#7) change the Primary Region, and drop the old regions.  Note that, as soon as the database is permitted to place replicas in a new region, the system will start rebalancing replicas to take advantage of the newly available resources.&lt;/p&gt;

&lt;p&gt;As in the last exercise, we could adjust these two cluster settings to speed up replica migration and reduce the time until the system reaches a new stable distribution of data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kv.snapshot_rebalance.max_rate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kv.snapshot_recovery.max_rate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(That's the optional step #5 in our process overview.) However, as we’ve already covered that in detail, we’ll skip it for purposes of this demonstration.&lt;/p&gt;

&lt;p&gt;First, let’s add the region &lt;strong&gt;us-east4&lt;/strong&gt; to the cluster, and make it the Primary Region.&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%2Fhbowxe8tt9vfqzspnlvk.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%2Fhbowxe8tt9vfqzspnlvk.png" alt="Change the Primary Region" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can drop the &lt;strong&gt;us-central1&lt;/strong&gt; region from the cluster, forcing the system to move all of the replicas off those nodes, as it is no longer a legitimate region for this database to keep replicas.&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%2Fl01r0f0ze8n8xt3l2len.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%2Fl01r0f0ze8n8xt3l2len.png" alt="Drop a region" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Monitoring the DB Console, we can watch the replicas and workload shift to the new region.  Finally, all the replicas and leaseholders have moved out of us-central1 and us-east1 has assumed its share of the load and hosts all of the leaseholders.&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%2Fdpcqyzhe3nfehlcejc4q.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%2Fdpcqyzhe3nfehlcejc4q.png" alt="Queries per node" width="800" height="424"&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.amazonaws.com%2Fuploads%2Farticles%2Flxok8qrvhwcbe04hdtv5.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%2Flxok8qrvhwcbe04hdtv5.png" alt="Leaseholder Locality" width="800" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, our application is still connected to the old primary region, &lt;strong&gt;us-central1&lt;/strong&gt;.  The application continues to run, but at somewhat lower performance.  The nodes of &lt;strong&gt;us-central1&lt;/strong&gt; can still accept SQL connections and serve queries, but the leaseholders and data are all now in another region; all the queries take longer because the gateway node in &lt;strong&gt;us-central1&lt;/strong&gt; has to communicate with leaseholder(s) in &lt;strong&gt;us-east4&lt;/strong&gt;.  Assuming we’re going to keep running the application where it’s currently deployed, we just need to update our application connection to point to our nodes in &lt;strong&gt;us-east4&lt;/strong&gt; (presumably via a load balancer for that region). &lt;/p&gt;

&lt;p&gt;Now’s the time to make that change (step #8), in order to minimize the window during which our application will see higher latency. &lt;/p&gt;

&lt;h2&gt;
  
  
  Finish adding and dropping Regions
&lt;/h2&gt;

&lt;p&gt;Now let’s add the other 2 new regions to the database, and drop the two regions that we want to evacuate:&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%2F09jhy044v85thyhonndz.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%2F09jhy044v85thyhonndz.png" alt="Add and drop regions" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, we verify the database configuration is what we expect:&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%2Fzj2tvv2alk6ectazlglo.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%2Fzj2tvv2alk6ectazlglo.png" alt="Updated Zone Configuration" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Monitoring the database distribution, we see after a time (about 2 hours, in this case) that all the replicas have completed their migration from the old regions to the new:&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%2Ff0xp46kbrzs19z5onv55.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%2Ff0xp46kbrzs19z5onv55.png" alt="Replicas Migrating to new Regions" width="800" height="294"&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.amazonaws.com%2Fuploads%2Farticles%2Fs7au8dojvigeigrhh0zb.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%2Fs7au8dojvigeigrhh0zb.png" alt="Updated Replica Distribution" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shrinking the Cluster
&lt;/h2&gt;

&lt;p&gt;Our database is completely migrated, the nodes in our original 3 regions are empty (except for replicas of system ranges), and we’re ready to retire those nodes (step #9).&lt;/p&gt;

&lt;p&gt;Following the documentation for how to decommission nodes from a CockroachDB cluster, we first drain the nodes we’re going to retire with the &lt;code&gt;cockroach node drain&lt;/code&gt; command, then decommission each node with &lt;code&gt;cockroach node decommission&lt;/code&gt;.  We expect this to go quickly, as nearly all replicas and leaseholders have already been moved off of these nodes.&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%2Fi6kozchmk72kq6cwgk8q.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%2Fi6kozchmk72kq6cwgk8q.png" alt="Draining Nodes" width="800" height="851"&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.amazonaws.com%2Fuploads%2Farticles%2Fmy53fr4yw8eh9w1v7hhy.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%2Fmy53fr4yw8eh9w1v7hhy.png" alt="Decommissioning Nodes" width="800" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have the end result we were aiming for: the cluster occupies a completely new set of nodes, and during the migration did not experience any downtime, only a short period of slightly degraded performance.&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%2Fbem68b2wmpx8r4xbyj71.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%2Fbem68b2wmpx8r4xbyj71.png" alt="Final Node Map" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the two cluster migration exercises we’ve documented, we’ve demonstrated CockroachDB’s ability to handle some demanding real-world customer requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scale a cluster up or down by adding or removing nodes&lt;/li&gt;
&lt;li&gt;add or remove a cloud Region to or from an existing cluster&lt;/li&gt;
&lt;li&gt;scale or migrate the cluster while it’s live and serving data, with no downtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And we’ve illustrated some of the features that make CockroachDB attractive to both Application developers, Database administrators, and Site Reliability Engineers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;high availability, even during cloud provisioning&lt;/li&gt;
&lt;li&gt;versatile and responsive data placement via SQL commands&lt;/li&gt;
&lt;li&gt;DB Console monitoring of runtime behavior at different levels of the software stack: 

&lt;ul&gt;
&lt;li&gt;client connections, &lt;/li&gt;
&lt;li&gt;SQL execution, &lt;/li&gt;
&lt;li&gt;inter-node network traffic, &lt;/li&gt;
&lt;li&gt;data replication and relocation, &lt;/li&gt;
&lt;li&gt;disk operations, &lt;/li&gt;
&lt;li&gt;CPU and memory usage, &lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We invite you to learn more about CockroachDB from &lt;a href="https://www.cockroachlabs.com/docs/" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt; and from our online courses at &lt;a href="https://www.cockroachlabs.com/cockroach-university/" rel="noopener noreferrer"&gt;Cockroach University&lt;/a&gt;, to read about the &lt;a href="https://www.cockroachlabs.com/22-2-launch/" rel="noopener noreferrer"&gt;launch of version 22.2&lt;/a&gt;, and to &lt;a href="https://cockroachlabs.cloud/" rel="noopener noreferrer"&gt;try out CockroachDB&lt;/a&gt; if you haven’t already. (You can get started with a &lt;a href="https://www.cockroachlabs.com/get-started-cockroachdb-23/" rel="noopener noreferrer"&gt;free cloud account&lt;/a&gt;!)&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Testing client reconnection to a CockroachDB Dedicated Cluster</title>
      <dc:creator>Greg Goodman</dc:creator>
      <pubDate>Thu, 27 Jan 2022 23:43:11 +0000</pubDate>
      <link>https://dev.to/gregcrl/client-reconnection-with-a-cockroachdb-dedicated-cluster-1dac</link>
      <guid>https://dev.to/gregcrl/client-reconnection-with-a-cockroachdb-dedicated-cluster-1dac</guid>
      <description>&lt;h2&gt;
  
  
  Purpose
&lt;/h2&gt;

&lt;p&gt;This post considers several approaches to testing an application’s error handling of the failure of the gateway node in a CockroachDB Dedicated cluster, and proposes some better approaches that could be supported by Cockroach Labs.&lt;/p&gt;

&lt;h2&gt;
  
  
  CockroachDB Availability Guarantee
&lt;/h2&gt;

&lt;p&gt;CockroachDB guarantees that a cluster will remain able to serve clients and read/write data, even if some portion of the infrastructure becomes unavailable.  Network connections may fail; individual nodes may crash; whole regions of the cloud may become inaccessible.  But, within the limits of the defined replication scheme and quorum-based voting, the cluster stays up and running, handling client connections, planning and executing SQL queries, and storing and serving data.&lt;/p&gt;

&lt;p&gt;Note that this availability guarantee applies to the cluster as a whole, and to the data within it; no such guarantee is made about the health of any individual node in the cluster.  Individual nodes may be terminated for any number of reasons.  Because a CockroachDB cluster can &lt;em&gt;always&lt;/em&gt; survive the loss of a single node, clusters are typically upgraded via “rolling updates”, where one node at a time is shut down, outfitted with an updated executable, or VM, or node configuration, and then restarted.  If, during normal operation, a node becomes too busy, or unstable, or runs out of memory, it will shut itself down rather than become a burden on cluster performance.  In all these cases, the cluster accommodates the loss of the node, and reincorporates the node into the cluster when it comes back up. &lt;/p&gt;

&lt;h2&gt;
  
  
  Application Reconnection &amp;amp; Testing
&lt;/h2&gt;

&lt;p&gt;When a SQL client connects to the cluster, it connects to some arbitrary node (referred to as that client’s “gateway node”); which specific node is serving as gateway to that client doesn’t really matter.  Every operation submitted to the gateway node gets handled by the cluster, and could as easily have been submitted to any of the other nodes.&lt;/p&gt;

&lt;p&gt;Because a SQL client may be connected to any node of the cluster, and because any given node of a cluster might be terminated at any time, it is the responsibility of the application to detect any unexpected network disconnection from its gateway node, and to reconnect to some other node of the cluster.&lt;/p&gt;

&lt;p&gt;Code - especially error handling code - needs to be well tested.  Best practices dictate that testing be performed in an environment that’s as close as possible to the target production environment, and under conditions as close as possible to real-world conditions.  For an application intended to work with a CockroachDB Dedicated cluster, this means testing against a CockroachDB Dedicated cluster.  To make sure that the orchestration and Load Balancer are the same as the target environment, the test should be conducted with a Dedicated cluster hosted in the same cloud provider as the production system.&lt;/p&gt;

&lt;p&gt;The condition we’re trying to detect is unexpected termination of the gateway node.  The appropriate test scenario is to kill the gateway node and see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the client detects the resultant loss of connection&lt;/li&gt;
&lt;li&gt;and then attempts to reconnect to the cluster&lt;/li&gt;
&lt;li&gt;whether the reconnection is successful, and to which node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a self-hosted cluster, we could arrange to have access to the host machine and terminate the node however we like: kill the &lt;strong&gt;cockroach&lt;/strong&gt; process, or terminate the Kubernetes pod the process is running in, or shut down the host altogether.  In a Dedicated cluster, we don’t have access to any of those options.  &lt;/p&gt;

&lt;p&gt;So the question to answer is "How do we kill a specific node in a managed cluster?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1: Command Termination
&lt;/h2&gt;

&lt;p&gt;The CockroachDB reference documentation offers a possibility: a SQL utility function to kill the gateway node. &lt;strong&gt;&lt;em&gt;crdb_internal.force_panic(msg)&lt;/em&gt;&lt;/strong&gt; lets a SQL client force the &lt;strong&gt;cockroach&lt;/strong&gt; process to panic, and to output the passed error message to the log.&lt;/p&gt;

&lt;p&gt;Unfortunately, while that function is available to clients of a self-hosted cluster, it is not available in a Dedicated cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; select * from crdb_internal.force_panic('TESTING!');
ERROR: crdb_internal.force_panic(): insufficient privilege
SQLSTATE: 42501
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution 2: Involve the Site Reliability Engineer
&lt;/h2&gt;

&lt;p&gt;CockroachDB Site Reliability Engineers (SREs) have complete access to all the levels of a Dedicated cluster, and can easily create the necessary test conditions.  There are a number of advantages to having the SRE involved in a system test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SRE can readily create any number of different failure scenarios, allowing us to test our application’s handling of a range of subtly different failure recovery behaviors.&lt;/li&gt;
&lt;li&gt;The SRE is the go-to resource for handling problems with the cluster; having the SRE on hand during testing minimizes the risks associated with disruptive operations.&lt;/li&gt;
&lt;li&gt;The SRE also has access to the best available introspection into the cluster; the test exercise can produce much more detailed results if the SRE is involved than otherwise.&lt;/li&gt;
&lt;li&gt;The testing we want to do may produce failures that would normally alert the SRE and initiate diagnosis and/or restoration.  Having the SRE involved in the test would prevent unnecessary scrambling to respond to the errors we generate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the SRE is not readily available for this task, for a number of reasons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SRE’s job is to keep managed clusters healthy and running, not to break them, and their tools and processes are all geared toward preventing failures, rather than creating them.  Involving the SRE in this sort of testing currently requires that they step outside their established practices and procedures. Making this a standard part of their job would require significant modification of their tools, processes, and procedures.&lt;/li&gt;
&lt;li&gt;The SRE’s goal is to make the management of a fleet of clusters efficient, scalable, and profitable.  This is accomplished by standardizing processes, automating the job as much as possible, and minimizing variability.  Engaging the SRE on the customer’s schedule to perform customer-specified actions in service of a custom test plan is the exact opposite of the type of activity the SRE is meant to do.&lt;/li&gt;
&lt;li&gt;SRE time is valuable.  If SREs are to engage directly with customers, those customers need to represent significant revenue to Cockroach Labs.  That means that the SRE would likely only be available to execute test plans for s specific tier of customer, making this an insufficiently general solution to the problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cockroach Labs SREs are dedicated professionals who do what’s necessary in service of their assigned objective... but that objective is the implementation of a profitable practice to deliver a reliable managed service. Custom work and one-off operations are contrary to the primary goals of the SRE team.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3: Test on a Self-hosted Cluster
&lt;/h2&gt;

&lt;p&gt;While the ideal test environment is one that’s identical to the production environment, the difficulty of performing this specific test on a Dedicated cluster makes it tempting to simply download a copy of &lt;strong&gt;cockroach&lt;/strong&gt;, spin up a self-hosted cluster, and test with that.  The option offers some advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s cheap; CockroachDB is Open Source and free to download (although some features are only available with an Enterprise License).&lt;/li&gt;
&lt;li&gt;The customer has complete control over the test cluster, and can do anything with it that the SRE can do with the managed cluster.&lt;/li&gt;
&lt;li&gt;And, of course, this option has the benefit (from the SRE’s perspective) of requiring no additional engagement with Cockroach Labs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some customers are able and willing to test with a self-hosted cluster, but for many it is not an acceptable option.&lt;/p&gt;

&lt;p&gt;The first objection customers often raise when this option is presented is “We’re paying for managed service, and shouldn’t have to manage our own cluster to validate that everything works.”  Customers subscribe to a managed service for different reasons; some don’t have the infrastructure resources or technical expertise to manage their own infrastructure, while others don’t have the time, as their staff are fully occupied with other tasks. Having to manage their own testing infrastructure robs them of at least some of the benefit of a managed service.&lt;/p&gt;

&lt;p&gt;Another more technical objection to this testing option is that a self-hosted cluster is not identical to a CockroachDB Dedicated cluster.  It’s not orchestrated or configured the same way; its networking and security are different, possibly very different; the test cluster may not exactly replicate the behavior of the production cluster, which could produce unrepresentative - even misleading - test results.&lt;/p&gt;

&lt;p&gt;And finally, customers in highly regulated industries, such as financial services or healthcare, or government and military contracting, may be required to test their recovery plans in production for auditing or certification purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 4: Emulate a Failure in the Dedicated Cluster
&lt;/h2&gt;

&lt;p&gt;That leaves us with the option of testing our application against our production CockroachDB Dedicated cluster, and simulating the node failure rather than inducing an actual one.  &lt;/p&gt;

&lt;p&gt;The failure mode we need to detect is the loss of connection between our application and the gateway node; the recovery we need to effect is reconnection to some other node of the cluster.  To accomplish this, we have to sever the connection in a way that looks to the client like the gateway node has terminated.  And when we reconnect to the cluster, it can’t be to the same node, as the test scenario is that the original node is no longer available.&lt;/p&gt;

&lt;p&gt;At first glance, this looks reasonably straightforward.  If we can identify the specific connection between the client and the gateway node, we can kill it (with, for example, &lt;strong&gt;tcpkill&lt;/strong&gt;) and prevent reconnection to the same node (with, for example, an &lt;strong&gt;iptables&lt;/strong&gt; rule).  All we need is the gateway node’s IP address.  And, in a self-hosted cluster, this is easy. The application can tell us the IP address of the node it’s connected to by fetching its &lt;strong&gt;node_id&lt;/strong&gt; with the SQL function &lt;strong&gt;crdb_internal.node_id()&lt;/strong&gt;, and querying its &lt;strong&gt;sql_address&lt;/strong&gt; from the &lt;strong&gt;crdb_internal.gossip_nodes&lt;/strong&gt; table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; select string_to_array(sql_address,':')[1] as host
-&amp;gt; from crdb_internal.gossip_nodes
-&amp;gt; where node_id in (select * from crdb_internal.node_id());
    host
-------------
  35.231.50.225

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, the nodes of a CockroachDB Dedicated cluster don’t have publicly accessible IP addresses.  The &lt;strong&gt;crdb_internal.gossip_nodes.sql_address&lt;/strong&gt; is a hostname that can’t be resolved outside the cluster’s private network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; select string_to_array(sql_address,':')[1] as host
-&amp;gt; from crdb_internal.gossip_nodes
-&amp;gt; where node_id in (select * from crdb_internal.node_id());
                          host
--------------------------------------------------------
  cockroachdb-2.cockroachdb.us-east1.svc.cluster.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only IP address the client ever sees is that of the Load Balancer.  Without an IP address, interdicting traffic to a specific node on the other side of a load balancer would require much more sophisticated networking management tools, deep inspection of packets… and might still be impossible with a properly secured cluster.&lt;/p&gt;

&lt;p&gt;In any case, simulating the failure of a single node in a CockroachDB Dedicated cluster - from outside that cluster - is decidedly non-trivial, and beyond the purview of many customers.&lt;/p&gt;

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

&lt;p&gt;The problem is real, and we do not currently have a good solution.  Developers have to test the error handling capabilities of their application code, and should or must perform those tests in an environment identical to their production environment.  There are a number of approaches to testing an application’s handling of an unexpectedly terminated node in a CockroachDB Dedicated cluster, but they are all sub-optimal and, for many customers, there is currently no acceptable solution.&lt;/p&gt;

&lt;p&gt;There are solutions that could be made available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CockroachDB Dedicated clusters could enable &lt;strong&gt;&lt;em&gt;crdb_internal.force_panic()&lt;/em&gt;&lt;/strong&gt; for sufficiently privileged users.&lt;/li&gt;
&lt;li&gt;The DB Console for Dedicated clusters could be enhanced to allow sufficiently privileged users to force certain kinds of failures, without raising alarms for the SRE to respond to.&lt;/li&gt;
&lt;li&gt;Cockroach Labs could expand the managed service to include customer-driven testing of various failure modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But all of these solutions would require design and implementation by Cockroach Labs. That level of effort will require prioritization and allocation of resources, which will have to be driven by customer demand.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
