{"action":"created","comment":{"url":"https://api.github.com/repos/apache/couchdb-documentation/pulls/comments/203327355","pull_request_review_id":138184210,"id":203327355,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMzMyNzM1NQ==","diff_hunk":"@@ -12,290 +12,490 @@\n \n .. _cluster/sharding:\n \n-========\n-Sharding\n-========\n+================\n+Shard Management\n+================\n \n .. _cluster/sharding/scaling-out:\n \n-Scaling out\n-===========\n+Introduction\n+------------\n \n-Normally you start small and grow over time. In the beginning you might do just\n-fine with one node, but as your data and number of clients grows, you need to\n-scale out.\n+A `shard\n+<https://en.wikipedia.org/wiki/Shard_(database_architecture)>`__ is a\n+horizontal partition of data in a database. Partitioning data into\n+shards and distributing copies of each shard (called \"shard replicas\" or\n+just \"replicas\") to different nodes in a cluster gives the data greater\n+durability against node loss. CouchDB clusters automatically shard\n+databases and distribute the subsections of documents that compose each\n+shard among nodes. Modifying cluster membership and sharding behavior\n+must be done manually.\n \n-For simplicity we will start fresh and small.\n+Shards and Replicas\n+~~~~~~~~~~~~~~~~~~~\n \n-Start ``node1`` and add a database to it. To keep it simple we will have 2\n-shards and no replicas.\n+How many shards and replicas each database has can be set at the global\n+level, or on a per-database basis. The relevant parameters are *q* and\n+*n*.\n \n-.. code-block:: bash\n+*q* is the number of database shards to maintain. *n* is the number of\n+copies of each document to distribute. The default value for n is 3,\n+and for q is 8. With q=8, the database is split into 8 shards. With\n+n=3, the cluster distributes three replicas of each shard. Altogether,\n+that's 24 shards for a single database. In a default 3-node cluster,\n+each node would receive 8 shards. In a 4-node cluster, each node would\n+receive 6 shards. We recommend in the general case that the number of\n+nodes in your cluster should be a multiple of n, so that shards are\n+distributed evenly.\n \n-    curl -X PUT \"http://xxx.xxx.xxx.xxx:5984/small?n=1&q=2\" --user daboss\n+CouchDB nodes have a ``etc/default.ini`` file with a section named\n+``[cluster]`` which looks like this:\n \n-If you look in the directory ``data/shards`` you will find the 2 shards.\n+::\n \n-.. code-block:: text\n+    [cluster]\n+    q=8\n+    n=3\n \n-    data/\n-    +-- shards/\n-    |   +-- 00000000-7fffffff/\n-    |   |    -- small.1425202577.couch\n-    |   +-- 80000000-ffffffff/\n-    |        -- small.1425202577.couch\n+These settings can be modified to set sharding defaults for all\n+databases, or they can be set on a per-database basis by specifying the\n+``q`` and ``n`` query parameters when the database is created. For\n+example:\n \n-Now, check the node-local ``_dbs_`` database. Here, the metadata for each\n-database is stored. As the database is called ``small``, there is a document\n-called ``small`` there. Let us look in it. Yes, you can get it with curl too:\n+.. code:: bash\n \n-.. code-block:: javascript\n+    $ curl -X PUT \"$COUCH_URL/database-name?q=4&n=2\"\n \n-    curl -X GET \"http://xxx.xxx.xxx.xxx:5986/_dbs/small\"\n+That creates a database that is split into 4 shards and 2 replicas,\n+yielding 8 shards distributed throughout the cluster.\n \n+Quorum\n+~~~~~~\n+\n+Depending on the size of the cluster, the number of shards per database,\n+and the number of shard replicas, not every node may have access to\n+every shard, but every node knows where all the replicas of each shard\n+can be found through CouchDB's internal shard map.\n+\n+Each request that comes in to a CouchDB cluster is handled by any one\n+random coordinating node. This coordinating node proxies the request to\n+the other nodes that have the relevant data, which may or may not\n+include itself. The coordinating node sends a response to the client\n+once a `quorum\n+<https://en.wikipedia.org/wiki/Quorum_(distributed_computing)>`__ of\n+database nodes have responded; 2, by default.\n+\n+The size of the required quorum can be configured at\n+request time by setting the ``r`` parameter for document and view\n+reads, and the ``w`` parameter for document writes. For example, here\n+is a request that specifies that at least two nodes must respond in\n+order to establish a quorum:\n+\n+.. code:: bash\n+\n+    $ curl \"$COUCH_URL:5984/<doc>?r=2\"\n+\n+Here is a similar example for writing a document:\n+\n+.. code:: bash\n+\n+    $ curl -X PUT \"$COUCH_URL:5984/<doc>?w=2\" -d '{...}'\n+\n+Setting ``r`` or ``w`` to be equal to ``n`` (the number of replicas)\n+means you will only receive a response once all nodes with relevant\n+shards have responded or timed out, and as such this approach does not\n+guarantee `ACIDic consistency\n+<https://en.wikipedia.org/wiki/ACID#Consistency>`__. Setting ``r`` or\n+``w`` to 1 means you will receive a response after only one relevant\n+node has responded.\n+\n+.. _cluster/sharding/move:\n+\n+Moving a shard\n+--------------\n+\n+This section describes how to manually place and replace shards, and how\n+to set up placement rules to assign shards to specific nodes. These\n+activities are critical steps when you determine your cluster is too big\n+or too small, and want to resize it successfully, or you have noticed\n+from server metrics that database/shard layout is non-optimal and you\n+have some \"hot spots\" that need resolving.\n+\n+Consider a three node cluster with q=8 and n=3. Each database has 24\n+shards, distributed across the three nodes. If you add a fourth node to\n+the cluster, CouchDB will not redistribute existing database shards to\n+it. This leads to unbalanced load, as the new node will only host shards\n+for databases created after it joined the cluster. To balance the\n+distribution of shards from existing databases, they must be moved\n+manually.\n+\n+Moving shards between nodes in a cluster involves the following steps:\n+\n+1. Copy the shard(s) and any secondary index shard(s) onto the target node.\n+2. Set the target node to maintenance mode.\n+3. Update cluster metadata to reflect the new target shard(s).\n+4. Monitor internal replication to ensure up-to-date shard(s).\n+5. Clear the target node's maintenance mode.\n+6. Update cluster metadata again to remove the source shard(s)\n+7. Remove the shard file(s) and secondary index file(s) from the source node.\n+\n+Copying shard files\n+~~~~~~~~~~~~~~~~~~~\n+\n+Shard files live in the ``data/shards`` directory of your CouchDB\n+install. Within those subdirectories are the shard files themselves. For\n+instance, for a q=8 database called abc, here is its database shard\n+files:\n+\n+::\n+\n+  data/shards/00000000-1fffffff/abc.1529362187.couch\n+  data/shards/20000000-3fffffff/abc.1529362187.couch\n+  data/shards/40000000-5fffffff/abc.1529362187.couch\n+  data/shards/60000000-7fffffff/abc.1529362187.couch\n+  data/shards/80000000-9fffffff/abc.1529362187.couch\n+  data/shards/a0000000-bfffffff/abc.1529362187.couch\n+  data/shards/c0000000-dfffffff/abc.1529362187.couch\n+  data/shards/e0000000-ffffffff/abc.1529362187.couch\n+\n+Since they are just files, you can use ``cp``, ``rsync``,\n+``scp`` or other command to copy them from one node to another. For\n+example:\n+\n+.. code:: bash\n+\n+    # one one machine\n+    $ mkdir -p data/shards/<range>\n+    # on the other\n+    $ scp <couch-dir>/data/shards/<range>/<database>.<datecode>.couch <node>:<couch-dir>/data/shards/<range>/\n+\n+Secondary indexes (including JavaScript views, Erlang views and Mango\n+indexes) are also sharded, and their shards should be moved to save the\n+new node the effort of rebuilding the view. View shards live in\n+``data/.shards``. For example:\n+\n+::\n+\n+  data/.shards\n+  data/.shards/e0000000-ffffffff/_replicator.1518451591_design\n+  data/.shards/e0000000-ffffffff/_replicator.1518451591_design/mrview\n+  data/.shards/e0000000-ffffffff/_replicator.1518451591_design/mrview/3e823c2a4383ac0c18d4e574135a5b08.view\n+  data/.shards/c0000000-dfffffff\n+  data/.shards/c0000000-dfffffff/_replicator.1518451591_design\n+  data/.shards/c0000000-dfffffff/_replicator.1518451591_design/mrview\n+  data/.shards/c0000000-dfffffff/_replicator.1518451591_design/mrview/3e823c2a4383ac0c18d4e574135a5b08.view\n+  ...\n+\n+.. warning::\n+    Technically, copying database and secondary index\n+    shards is optional. If you proceed to the next step without\n+    performing\n+    this data copy, CouchDB will use internal replication to populate\n+    the\n+    newly added shard replicas. However, this process can be very slow,\n+    especially on a busy cluster—which is why we recommend performing this\n+    manual data copy first.\n+\n+Set the target node to ``true`` maintenance mode\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+Before we tell CouchDB about the new shards on the node in question, we\n+need to set the node to ``true`` maintenance mode. This special mode\n+instructs CouchDB to return a ``404 Not Found`` response on the ``/_up``\n+endpoint, and ensures it not participate in normal interactive clustered\n+requests for its shards. A properly configured load balancer that uses\n+``GET /_up`` to check the health of nodes will detect this 404 and\n+remove that node from the backend target, preventing any HTTP requests\n+from being sent to that node. An example HAProxy configuration to use\n+the ``/_up`` endpoint is as follows:\n+\n+::\n+\n+  http-check disable-on-404\n+  option httpchk GET /_up\n+\n+If you do not set maintenance mode, or the load balancer ignores this\n+maintenance mode status, after the next step is performed the cluster\n+may return incorrect responses when consulting the node in question. You\n+don't want this! In the next steps, we will ensure that this shard is\n+up-to-date before allowing it to participate in end-user requests.\n+\n+To set true maintenance mode:\n+\n+.. code::bash\n+\n+    $ curl -X PUT -H \"Content-type: application/json\" \\\n+        http://localhost:5984/_node/<nodename>/_config/couchdb/maintenance_mode \\\n+        -d \"\\\"true\\\"\"\n+\n+Then, verify that the node is in maintenance mode by performing a ``GET\n+/_up`` on that node's individual endpoint:\n+\n+.. code::bash\n+\n+    $ curl -v http://localhost:5984/_up\n+    …\n+    < HTTP/1.1 404 Object Not Found\n+    …\n+    {\"status\":\"maintenance_mode\"}\n+\n+Finally, check that your load balancer has removed the node from the\n+pool of available backend nodes.\n+\n+Updating cluster metadata to reflect the move\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+Now we need to tell CouchDB that the target node (which must already be\n+joined to the cluster) should be hosting shard replicas for a given\n+database.\n+\n+To update the cluster metadata, use the special ``/_dbs`` database,\n+which is an internal CouchDB database that maps databases to shards and\n+nodes. It is accessible only via a node's private port, usually at port\n+5986. By default, this port is only available on the localhost interface\n+for security purposes.\n+\n+First, retrieve the database's current metadata:\n+\n+.. code:: bash\n+\n+    $ curl localhost:5986/_dbs/{name}\n     {\n-        \"_id\": \"small\",\n-        \"_rev\": \"1-5e2d10c29c70d3869fb7a1fd3a827a64\",\n-        \"shard_suffix\": [\n-            46,\n-            49,\n-            52,\n-            50,\n-            53,\n-            50,\n-            48,\n-            50,\n-            53,\n-            55,\n-            55\n+      \"_id\": \"{name}\",\n+      \"_rev\": \"1-e13fb7e79af3b3107ed62925058bfa3a\",\n+      \"shard_suffix\": [46, 49, 53, 51, 48, 50, 51, 50, 53, 50, 54],\n+      \"changelog\": [\n+        [\"add\", \"00000000-1fffffff\", \"node1@xxx.xxx.xxx.xxx\"],\n+        [\"add\", \"00000000-1fffffff\", \"node2@xxx.xxx.xxx.xxx\"],\n+        [\"add\", \"00000000-1fffffff\", \"node3@xxx.xxx.xxx.xxx\"],\n+        …\n+      ],\n+      \"by_node\": {\n+        \"node1@xxx.xxx.xxx.xxx\": [\n+          \"00000000-1fffffff\",\n+          …\n         ],\n-        \"changelog\": [\n-        [\n-            \"add\",\n-            \"00000000-7fffffff\",\n-            \"node1@xxx.xxx.xxx.xxx\"\n+        …\n+      },\n+      \"by_range\": {\n+        \"00000000-1fffffff\": [\n+          \"node1@xxx.xxx.xxx.xxx\",\n+          \"node2@xxx.xxx.xxx.xxx\",\n+          \"node3@xxx.xxx.xxx.xxx\"\n         ],\n-        [\n-            \"add\",\n-            \"80000000-ffffffff\",\n-            \"node1@xxx.xxx.xxx.xxx\"\n-        ]\n-        ],\n-        \"by_node\": {\n-            \"node1@xxx.xxx.xxx.xxx\": [\n-                \"00000000-7fffffff\",\n-                \"80000000-ffffffff\"\n-            ]\n-        },\n-        \"by_range\": {\n-            \"00000000-7fffffff\": [\n-                \"node1@xxx.xxx.xxx.xxx\"\n-            ],\n-            \"80000000-ffffffff\": [\n-                \"node1@xxx.xxx.xxx.xxx\"\n-            ]\n-        }\n+        …\n+      }\n     }\n \n-* ``_id`` The name of the database.\n-* ``_rev`` The current revision of the metadata.\n-* ``shard_suffix`` The numbers after small and before .couch. This is seconds\n-  after UNIX epoch when the database was created. Stored as ASCII characters.\n-* ``changelog`` Self explaining. Mostly used for debugging.\n-* ``by_node`` List of shards on each node.\n-* ``by_range`` On which nodes each shard is.\n+Here is a brief anatomy of that document:\n+\n+-  ``_id``: The name of the database.\n+-  ``_rev``: The current revision of the metadata.\n+-  ``shard_suffix``: A timestamp of the database's creation, marked as\n+   seconds after the Unix epoch mapped to the codepoints for ASCII\n+   numerals.\n+-  ``changelog``: History of the database's shards.\n+-  ``by_node``: List of shards on each node.\n+-  ``by_range``: On which nodes each shard is.\n+\n+To reflect the shard move in the metadata, there are three steps:\n \n-Nothing here, nothing there, a shard in my sleeve\n--------------------------------------------------\n+1. Add appropriate changelog entries.\n+2. Update the ``by_node`` entries.\n+3. Update the ``by_range`` entries.\n \n-Start node2 and add it to the cluster. Check in ``/_membership`` that the\n-nodes are talking with each other.\n+As of this writing, this process must be done manually. **WARNING: Be\n+very careful! Mistakes during this process can irreperably corrupt the\n+cluster!**\n \n-If you look in the directory ``data`` on node2, you will see that there is no\n-directory called shards.\n+To add a shard to a node, add entries like this to the database\n+metadata's ``changelog`` attribute:\n \n-Use curl to change the ``_dbs/small`` node-local document for small, so it\n-looks like this:\n+.. code:: json\n \n-.. code-block:: javascript\n+    [\"add\", \"<range>\", \"<node-name>\"]\n+\n+The ``<range>`` is the specific shard range for the shard. The ``<node-\n+name>`` should match the name and address of the node as displayed in\n+``GET /_membership`` on the cluster.\n+\n+.. warning::\n+    When removing a shard from a node, specifying ``remove`` instead of ``add``.\n+\n+Once you have figured out the new changelog entries, you will need to\n+update the ``by_node`` and ``by_range`` to reflect who is storing what\n+shards. The data in the changelog entries and these attributes must\n+match. If they do not, the database may become corrupted.\n+\n+Continuing our example, here is an updated version of the metadata above\n+that adds shards to an additional node called ``node4``:\n+\n+.. code:: json\n \n     {\n-        \"_id\": \"small\",\n-        \"_rev\": \"1-5e2d10c29c70d3869fb7a1fd3a827a64\",\n-        \"shard_suffix\": [\n-            46,\n-            49,\n-            52,\n-            50,\n-            53,\n-            50,\n-            48,\n-            50,\n-            53,\n-            55,\n-            55\n-        ],\n-        \"changelog\": [\n-        [\n-            \"add\",\n-            \"00000000-7fffffff\",\n-            \"node1@xxx.xxx.xxx.xxx\"\n-        ],\n-        [\n-            \"add\",\n-            \"80000000-ffffffff\",\n-            \"node1@xxx.xxx.xxx.xxx\"\n-        ],\n-        [\n-            \"add\",\n-            \"00000000-7fffffff\",\n-            \"node2@yyy.yyy.yyy.yyy\"\n+      \"_id\": \"{name}\",\n+      \"_rev\": \"1-e13fb7e79af3b3107ed62925058bfa3a\",\n+      \"shard_suffix\": [46, 49, 53, 51, 48, 50, 51, 50, 53, 50, 54],\n+      \"changelog\": [\n+        [\"add\", \"00000000-1fffffff\", \"node1@xxx.xxx.xxx.xxx\"],\n+        [\"add\", \"00000000-1fffffff\", \"node2@xxx.xxx.xxx.xxx\"],\n+        [\"add\", \"00000000-1fffffff\", \"node3@xxx.xxx.xxx.xxx\"],\n+        …\n+        [\"add\", \"00000000-1fffffff\", \"node4@xxx.xxx.xxx.xxx\"]\n+      ],\n+      \"by_node\": {\n+        \"node1@xxx.xxx.xxx.xxx\": [\n+          \"00000000-1fffffff\",\n+          …\n         ],\n-        [\n-            \"add\",\n-            \"80000000-ffffffff\",\n-            \"node2@yyy.yyy.yyy.yyy\"\n+        …\n+        \"node4@xxx.xxx.xxx.xxx\": [\n+          \"00000000-1fffffff\"\n         ]\n+      },\n+      \"by_range\": {\n+        \"00000000-1fffffff\": [\n+          \"node1@xxx.xxx.xxx.xxx\",\n+          \"node2@xxx.xxx.xxx.xxx\",\n+          \"node3@xxx.xxx.xxx.xxx\",\n+          \"node4@xxx.xxx.xxx.xxx\"\n         ],\n-        \"by_node\": {\n-            \"node1@xxx.xxx.xxx.xxx\": [\n-                \"00000000-7fffffff\",\n-                \"80000000-ffffffff\"\n-            ],\n-            \"node2@yyy.yyy.yyy.yyy\": [\n-                \"00000000-7fffffff\",\n-                \"80000000-ffffffff\"\n-            ]\n-        },\n-        \"by_range\": {\n-            \"00000000-7fffffff\": [\n-                \"node1@xxx.xxx.xxx.xxx\",\n-                \"node2@yyy.yyy.yyy.yyy\"\n-            ],\n-            \"80000000-ffffffff\": [\n-                \"node1@xxx.xxx.xxx.xxx\",\n-                \"node2@yyy.yyy.yyy.yyy\"\n-            ]\n-        }\n+        …\n+      }\n     }\n \n-After PUTting this document, it's like magic: the shards are now on node2 too!\n-We now have ``n=2``!\n+Now you can ``PUT`` this new metadata:\n \n-If the shards are large, then you can copy them over manually and only have\n-CouchDB sync the changes from the last minutes instead.\n+.. code:: bash\n \n-.. _cluster/sharding/move:\n+    $ curl -X PUT $COUCH_URL:5986/_dbs/{name} -d '{...}'\n+\n+Monitor internal replication to ensure up-to-date shard(s)\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+After you complete the previous step, as soon as CouchDB receives a\n+write request for a shard on the target node, CouchDB will check if the\n+target node's shard(s) are up to date. If it finds they are not up to\n+date, it will trigger an internal replication job to complete this task.\n+You can observe this happening by triggering a write to the database\n+(update a document, or create a new one), while monitoring the\n+``/_node/<nodename>/_system`` endpoint, which includes the\n+``internal_replication_jobs`` metric.\n+\n+Once this metric has returned to the baseline from before you wrote the\n+document, or is zero (0., it is safe to proceed.\n+\n+Clear the target node's maintenance mode\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+You can now let the node back into the rotation for load balancing by\n+putting ``\"false\"`` to the maintenance mode configuration endpoint, just\n+as in step 2.\n+\n+Verify that the node is in not maintenance mode by performing a ``GET\n+/_up`` on that node's individual endpoint.\n+\n+Finally, check that your load balancer has returned the node to the pool\n+of available backend nodes.\n \n-Moving Shards\n-=============\n+Update cluster metadata again to remove the source shard\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-Add, then delete\n-----------------\n+Now, remove the source shard from the shard map the same way that you\n+added the new target shard to the shard map in step 2. Be sure to add\n+the ``[\"remove\", <range>, <source-shard>]`` entry to the end of the\n+changelog as well as modifying both the by_node and by_range sections of\n+the database metadata document.\n \n-In the world of CouchDB there is no such thing as \"moving\" shards, only adding\n-and removing shard replicas.\n-You can add a new replica of a shard and then remove the old replica,\n-thereby creating the illusion of moving.\n-If you do this for a database that has ``n=1``,\n-you might be caught by the following mistake:\n+Remove the shard and secondary index files from the source node\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-#. Copy the shard onto a new node.\n-#. Update the metadata to use the new node.\n-#. Delete the shard on the old node.\n-#. Oh, no!: You have lost all writes made between 1 and 2.\n+Finally, you can remove the source shard by deleting its file from the\n+command line on the source host, along with any view shards:\n \n-To avoid this mistake, you always want to make sure\n-that both shards have been live for some time\n-and that the shard on your new node is fully caught up\n-before removing a shard on an old node.\n-Since \"moving\" is a more conceptually (if not technically)\n-accurate description of what you want to do,\n-we'll use that word in this documentation as well.\n+.. code::bash\n \n-Moving\n-------\n+    $ rm <couch-dir>/data/shards/<range>/<dbname>.<datecode>.couch\n+    $ rm -r <couch-dir>/data/.shards/<range>/<dbname>.<datecode>*\n \n-When you get to ``n=3`` you should start moving the shards instead of adding\n-more replicas.\n+Congratulations! You have moved a database shard. By adding and removing\n+database shards in this way, you can change the cluster's shard layout,\n+also known as a shard map.\n \n-We will stop on ``n=2`` to keep things simple. Start node number 3 and add it to\n-the cluster. Then create the directories for the shard on node3:\n+Specifying database placement\n+-----------------------------\n \n-.. code-block:: bash\n+Database shards can be configured to live solely on specific nodes using\n+placement rules.\n \n-    mkdir -p data/shards/00000000-7fffffff\n+First, each node must be labeled with a zone attribute. This defines\n+which zone each node is in. You do this by editing the node’s document\n+in the ``/nodes`` database, which is accessed through the “back-door”\n+(5986. port. Add a key value pair of the form:\n \n-And copy over ``data/shards/00000000-7fffffff/small.1425202577.couch`` from\n-node1 to node3. Do not move files between the shard directories as that will\n-confuse CouchDB!\n+::\n \n-Edit the database document in ``_dbs`` again. Make it so that node3 have a\n-replica of the shard ``00000000-7fffffff``. Save the document and let CouchDB\n-sync. If we do not do this, then writes made during the copy of the shard and\n-the updating of the metadata will only have ``n=1`` until CouchDB has synced.\n+    \"zone\": \"{zone-name}\"\n \n-Then update the metadata document so that node2 no longer have the shard\n-``00000000-7fffffff``. You can now safely delete\n-``data/shards/00000000-7fffffff/small.1425202577.couch`` on node 2.\n+Do this for all of the nodes in your cluster. For example:\n \n-The changelog is nothing that CouchDB cares about, it is only for the admins.\n-But for the sake of completeness, we will update it again. Use ``delete`` for\n-recording the removal of the shard ``00000000-7fffffff`` from node2.\n+.. code:: bash\n \n-Start node4, add it to the cluster and do the same as above with shard\n-``80000000-ffffffff``.\n+    $ curl -X PUT $COUCH_URL:5986/_nodes/<node-name> \\\n+        -d '{ \\\n+            \"_id\": \"<node-name>\",\n+            \"_rev\": \"<rev>\",\n+            \"zone\": \"<zone-name>\"\n+            }'\n \n-All documents added during this operation was saved and all reads responded to\n-without the users noticing anything.\n+In the local config file (``local.ini``) of each node, define a\n+consistent cluster-wide setting like:\n \n-.. _cluster/sharding/views:\n+::\n \n-Views\n-=====\n+    [cluster]\n+    placement = <zone-name-1>:2,<zone-name-2>:1\n \n-The views need to be moved together with the shards. If you do not, then\n-CouchDB will rebuild them and this will take time if you have a lot of\n-documents.\n+In this example, CouchDB will ensure that two replicas for a shard will\n+be hosted on nodes with the zone attribute set to ``<zone-name-1>`` and\n+one replica will be hosted on a new with the zone attribute set to\n+``<zone-name-2>``.\n \n-The views are stored in ``data/.shards``.\n+This approach is flexible, since you can also specify zones on a per-\n+database basis by specifying the placement setting as a query parameter\n+when the database is created, using the same syntax as the ini file:\n \n-It is possible to not move the views and let CouchDB rebuild the view every\n-time you move a shard. As this can take quite some time, it is not recommended.\n+.. code:: bash\n \n-.. _cluster/sharding/preshard:\n+    curl -X PUT $COUCH_URL:5984/<dbname>?zone=<zone>\n \n-Reshard? No, Preshard!\n-======================\n+Note that you can also use this system to ensure certain nodes in the\n+cluster do not host any replicas for newly created databases, by giving\n+them a zone attribute that does not appear in the ``[cluster]``\n+placement string.\n \n-Reshard? Nope. It cannot be done. So do not create databases with too few\n-shards.\n+Resharding a database to a new q value\n+--------------------------------------\n \n-If you can not scale out more because you set the number of shards too low, then\n-you need to create a new cluster and migrate over.\n+The q value for a database can only be set when the database is created,\n+precluding live resharding. Instead, to reshard a database, it must be\n+regenerated. Here are the steps:\n \n-#. Build a cluster with enough nodes to handle one copy of your data.\n-#. Create a database with the same name, n=1 and with enough shards so you do\n-   not have to do this again.\n-#. Set up 2 way replication between the 2 clusters.\n-#. Let it sync.\n-#. Tell clients to use both the clusters.\n-#. Add some nodes to the new cluster and add them as replicas.\n-#. Remove some nodes from the old cluster.\n-#. Repeat 6 and 7 until you have enough nodes in the new cluster to have 3\n-   replicas of every shard.\n-#. Redirect all clients to the new cluster\n-#. Turn off the 2 way replication between the clusters.\n-#. Shut down the old cluster and add the servers as new nodes to the new\n-   cluster.\n-#. Relax!\n+1. Create a temporary database with the desired shard settings, by\n+   specifying the q value as a query parameter during the PUT\n+   operation.\n+2. Replicate the primary database to the temporary one. Multiple\n+   replications may be required if the primary database is under\n+   active use.\n+3. Delete the primary database. **Make sure nobody is using it!**\n+4. Recreate the primary database with the desired shard settings.\n+5. Replicate the temporary back to the primary.\n+6. Delete the temporary database.","path":"src/cluster/sharding.rst","position":699,"original_position":699,"commit_id":"06febc2c599540d9d1af9bff1af79c18109b8982","original_commit_id":"06febc2c599540d9d1af9bff1af79c18109b8982","user":{"login":"mikerhodes","id":453210,"node_id":"MDQ6VXNlcjQ1MzIxMA==","avatar_url":"https://avatars1.githubusercontent.com/u/453210?v=4","gravatar_id":"","url":"https://api.github.com/users/mikerhodes","html_url":"https://github.com/mikerhodes","followers_url":"https://api.github.com/users/mikerhodes/followers","following_url":"https://api.github.com/users/mikerhodes/following{/other_user}","gists_url":"https://api.github.com/users/mikerhodes/gists{/gist_id}","starred_url":"https://api.github.com/users/mikerhodes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikerhodes/subscriptions","organizations_url":"https://api.github.com/users/mikerhodes/orgs","repos_url":"https://api.github.com/users/mikerhodes/repos","events_url":"https://api.github.com/users/mikerhodes/events{/privacy}","received_events_url":"https://api.github.com/users/mikerhodes/received_events","type":"User","site_admin":false},"body":"This more describes an \"offline\" resharding. Rather than having the note in step 3, to be safe, I think there should be extra instructions:\r\n\r\n1. Prior to (2), \"stop clients accessing the database\".\r\n2. After (5), \"clients can now access the database again\".\r\n\r\n","created_at":"2018-07-18T10:25:39Z","updated_at":"2018-07-18T10:34:45Z","html_url":"https://github.com/apache/couchdb-documentation/pull/268#discussion_r203327355","pull_request_url":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/apache/couchdb-documentation/pulls/comments/203327355"},"html":{"href":"https://github.com/apache/couchdb-documentation/pull/268#discussion_r203327355"},"pull_request":{"href":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268"}}},"pull_request":{"url":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268","id":180028184,"node_id":"MDExOlB1bGxSZXF1ZXN0MTgwMDI4MTg0","html_url":"https://github.com/apache/couchdb-documentation/pull/268","diff_url":"https://github.com/apache/couchdb-documentation/pull/268.diff","patch_url":"https://github.com/apache/couchdb-documentation/pull/268.patch","issue_url":"https://api.github.com/repos/apache/couchdb-documentation/issues/268","number":268,"state":"open","locked":false,"title":"Rewrite sharding documentation","user":{"login":"garbados","id":883103,"node_id":"MDQ6VXNlcjg4MzEwMw==","avatar_url":"https://avatars0.githubusercontent.com/u/883103?v=4","gravatar_id":"","url":"https://api.github.com/users/garbados","html_url":"https://github.com/garbados","followers_url":"https://api.github.com/users/garbados/followers","following_url":"https://api.github.com/users/garbados/following{/other_user}","gists_url":"https://api.github.com/users/garbados/gists{/gist_id}","starred_url":"https://api.github.com/users/garbados/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/garbados/subscriptions","organizations_url":"https://api.github.com/users/garbados/orgs","repos_url":"https://api.github.com/users/garbados/repos","events_url":"https://api.github.com/users/garbados/events{/privacy}","received_events_url":"https://api.github.com/users/garbados/received_events","type":"User","site_admin":false},"body":"<!-- Thank you for your contribution!\r\n\r\n     Please file this form by replacing the Markdown comments\r\n     with your text. If a section needs no action - remove it.\r\n\r\n     Also remember, that CouchDB uses the Review-Then-Commit (RTC) model\r\n     of code collaboration. Positive feedback is represented +1 from committers\r\n     and negative is a -1. The -1 also means veto, and needs to be addressed\r\n     to proceed. Once there are no objections, the PR can be merged by a\r\n     CouchDB committer.\r\n\r\n     See: http://couchdb.apache.org/bylaws.html#decisions for more info. -->\r\n\r\n## Overview\r\n\r\n<!-- Please give a short brief for the pull request,\r\n     what problem it solves or how it makes things better. -->\r\n\r\nThis WIP PR attempts to revisit the documentation on sharding to make it clearer, and to include detailed instructions for adding and removing nodes and shards.\r\n\r\n## Checklist\r\n\r\n- [x] Documentation is written and is accurate;\r\n- [x] `make check` passes with no errors\r\n- [ ] Update [rebar.config.script](https://github.com/apache/couchdb/blob/master/rebar.config.script) with the commit hash once this PR is rebased and merged\r\n","created_at":"2018-04-06T18:59:07Z","updated_at":"2018-07-18T10:34:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f859c5009b1cdfe6112fe97678bdc99048ece5bb","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268/commits","review_comments_url":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268/comments","review_comment_url":"https://api.github.com/repos/apache/couchdb-documentation/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/couchdb-documentation/issues/268/comments","statuses_url":"https://api.github.com/repos/apache/couchdb-documentation/statuses/06febc2c599540d9d1af9bff1af79c18109b8982","head":{"label":"garbados:update-sharding-docs","ref":"update-sharding-docs","sha":"06febc2c599540d9d1af9bff1af79c18109b8982","user":{"login":"garbados","id":883103,"node_id":"MDQ6VXNlcjg4MzEwMw==","avatar_url":"https://avatars0.githubusercontent.com/u/883103?v=4","gravatar_id":"","url":"https://api.github.com/users/garbados","html_url":"https://github.com/garbados","followers_url":"https://api.github.com/users/garbados/followers","following_url":"https://api.github.com/users/garbados/following{/other_user}","gists_url":"https://api.github.com/users/garbados/gists{/gist_id}","starred_url":"https://api.github.com/users/garbados/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/garbados/subscriptions","organizations_url":"https://api.github.com/users/garbados/orgs","repos_url":"https://api.github.com/users/garbados/repos","events_url":"https://api.github.com/users/garbados/events{/privacy}","received_events_url":"https://api.github.com/users/garbados/received_events","type":"User","site_admin":false},"repo":{"id":105791067,"node_id":"MDEwOlJlcG9zaXRvcnkxMDU3OTEwNjc=","name":"couchdb-documentation","full_name":"garbados/couchdb-documentation","owner":{"login":"garbados","id":883103,"node_id":"MDQ6VXNlcjg4MzEwMw==","avatar_url":"https://avatars0.githubusercontent.com/u/883103?v=4","gravatar_id":"","url":"https://api.github.com/users/garbados","html_url":"https://github.com/garbados","followers_url":"https://api.github.com/users/garbados/followers","following_url":"https://api.github.com/users/garbados/following{/other_user}","gists_url":"https://api.github.com/users/garbados/gists{/gist_id}","starred_url":"https://api.github.com/users/garbados/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/garbados/subscriptions","organizations_url":"https://api.github.com/users/garbados/orgs","repos_url":"https://api.github.com/users/garbados/repos","events_url":"https://api.github.com/users/garbados/events{/privacy}","received_events_url":"https://api.github.com/users/garbados/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/garbados/couchdb-documentation","description":"Apache CouchDB Documentation","fork":true,"url":"https://api.github.com/repos/garbados/couchdb-documentation","forks_url":"https://api.github.com/repos/garbados/couchdb-documentation/forks","keys_url":"https://api.github.com/repos/garbados/couchdb-documentation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/garbados/couchdb-documentation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/garbados/couchdb-documentation/teams","hooks_url":"https://api.github.com/repos/garbados/couchdb-documentation/hooks","issue_events_url":"https://api.github.com/repos/garbados/couchdb-documentation/issues/events{/number}","events_url":"https://api.github.com/repos/garbados/couchdb-documentation/events","assignees_url":"https://api.github.com/repos/garbados/couchdb-documentation/assignees{/user}","branches_url":"https://api.github.com/repos/garbados/couchdb-documentation/branches{/branch}","tags_url":"https://api.github.com/repos/garbados/couchdb-documentation/tags","blobs_url":"https://api.github.com/repos/garbados/couchdb-documentation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/garbados/couchdb-documentation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/garbados/couchdb-documentation/git/refs{/sha}","trees_url":"https://api.github.com/repos/garbados/couchdb-documentation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/garbados/couchdb-documentation/statuses/{sha}","languages_url":"https://api.github.com/repos/garbados/couchdb-documentation/languages","stargazers_url":"https://api.github.com/repos/garbados/couchdb-documentation/stargazers","contributors_url":"https://api.github.com/repos/garbados/couchdb-documentation/contributors","subscribers_url":"https://api.github.com/repos/garbados/couchdb-documentation/subscribers","subscription_url":"https://api.github.com/repos/garbados/couchdb-documentation/subscription","commits_url":"https://api.github.com/repos/garbados/couchdb-documentation/commits{/sha}","git_commits_url":"https://api.github.com/repos/garbados/couchdb-documentation/git/commits{/sha}","comments_url":"https://api.github.com/repos/garbados/couchdb-documentation/comments{/number}","issue_comment_url":"https://api.github.com/repos/garbados/couchdb-documentation/issues/comments{/number}","contents_url":"https://api.github.com/repos/garbados/couchdb-documentation/contents/{+path}","compare_url":"https://api.github.com/repos/garbados/couchdb-documentation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/garbados/couchdb-documentation/merges","archive_url":"https://api.github.com/repos/garbados/couchdb-documentation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/garbados/couchdb-documentation/downloads","issues_url":"https://api.github.com/repos/garbados/couchdb-documentation/issues{/number}","pulls_url":"https://api.github.com/repos/garbados/couchdb-documentation/pulls{/number}","milestones_url":"https://api.github.com/repos/garbados/couchdb-documentation/milestones{/number}","notifications_url":"https://api.github.com/repos/garbados/couchdb-documentation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/garbados/couchdb-documentation/labels{/name}","releases_url":"https://api.github.com/repos/garbados/couchdb-documentation/releases{/id}","deployments_url":"https://api.github.com/repos/garbados/couchdb-documentation/deployments","created_at":"2017-10-04T16:26:46Z","updated_at":"2017-10-04T16:26:49Z","pushed_at":"2018-07-18T08:18:44Z","git_url":"git://github.com/garbados/couchdb-documentation.git","ssh_url":"git@github.com:garbados/couchdb-documentation.git","clone_url":"https://github.com/garbados/couchdb-documentation.git","svn_url":"https://github.com/garbados/couchdb-documentation","homepage":"","size":5106,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"apache:master","ref":"master","sha":"d1595c46fce91802dbc93a35b3eb4ad419529ae7","user":{"login":"apache","id":47359,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ3MzU5","avatar_url":"https://avatars0.githubusercontent.com/u/47359?v=4","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":15993609,"node_id":"MDEwOlJlcG9zaXRvcnkxNTk5MzYwOQ==","name":"couchdb-documentation","full_name":"apache/couchdb-documentation","owner":{"login":"apache","id":47359,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ3MzU5","avatar_url":"https://avatars0.githubusercontent.com/u/47359?v=4","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/couchdb-documentation","description":"Apache CouchDB Documentation","fork":false,"url":"https://api.github.com/repos/apache/couchdb-documentation","forks_url":"https://api.github.com/repos/apache/couchdb-documentation/forks","keys_url":"https://api.github.com/repos/apache/couchdb-documentation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/couchdb-documentation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/couchdb-documentation/teams","hooks_url":"https://api.github.com/repos/apache/couchdb-documentation/hooks","issue_events_url":"https://api.github.com/repos/apache/couchdb-documentation/issues/events{/number}","events_url":"https://api.github.com/repos/apache/couchdb-documentation/events","assignees_url":"https://api.github.com/repos/apache/couchdb-documentation/assignees{/user}","branches_url":"https://api.github.com/repos/apache/couchdb-documentation/branches{/branch}","tags_url":"https://api.github.com/repos/apache/couchdb-documentation/tags","blobs_url":"https://api.github.com/repos/apache/couchdb-documentation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/couchdb-documentation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/couchdb-documentation/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/couchdb-documentation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/couchdb-documentation/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/couchdb-documentation/languages","stargazers_url":"https://api.github.com/repos/apache/couchdb-documentation/stargazers","contributors_url":"https://api.github.com/repos/apache/couchdb-documentation/contributors","subscribers_url":"https://api.github.com/repos/apache/couchdb-documentation/subscribers","subscription_url":"https://api.github.com/repos/apache/couchdb-documentation/subscription","commits_url":"https://api.github.com/repos/apache/couchdb-documentation/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/couchdb-documentation/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/couchdb-documentation/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/couchdb-documentation/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/couchdb-documentation/contents/{+path}","compare_url":"https://api.github.com/repos/apache/couchdb-documentation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/couchdb-documentation/merges","archive_url":"https://api.github.com/repos/apache/couchdb-documentation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/couchdb-documentation/downloads","issues_url":"https://api.github.com/repos/apache/couchdb-documentation/issues{/number}","pulls_url":"https://api.github.com/repos/apache/couchdb-documentation/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/couchdb-documentation/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/couchdb-documentation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/couchdb-documentation/labels{/name}","releases_url":"https://api.github.com/repos/apache/couchdb-documentation/releases{/id}","deployments_url":"https://api.github.com/repos/apache/couchdb-documentation/deployments","created_at":"2014-01-17T08:00:08Z","updated_at":"2018-07-17T15:01:19Z","pushed_at":"2018-07-18T08:18:58Z","git_url":"git://github.com/apache/couchdb-documentation.git","ssh_url":"git@github.com:apache/couchdb-documentation.git","clone_url":"https://github.com/apache/couchdb-documentation.git","svn_url":"https://github.com/apache/couchdb-documentation","homepage":"","size":5095,"stargazers_count":56,"watchers_count":56,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":103,"mirror_url":null,"archived":false,"open_issues_count":14,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":103,"open_issues":14,"watchers":56,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268"},"html":{"href":"https://github.com/apache/couchdb-documentation/pull/268"},"issue":{"href":"https://api.github.com/repos/apache/couchdb-documentation/issues/268"},"comments":{"href":"https://api.github.com/repos/apache/couchdb-documentation/issues/268/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/couchdb-documentation/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/couchdb-documentation/pulls/268/commits"},"statuses":{"href":"https://api.github.com/repos/apache/couchdb-documentation/statuses/06febc2c599540d9d1af9bff1af79c18109b8982"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"mikerhodes"}}