<p>My MongoDB sharded cluster ingestion performances don't scale up when adding a new shard.</p>

<p>I have a small cluster setup with 1 mongos + 1 config replica set (3 nodes) + N shards replica sets (3 nodes each).</p>

<p>Mongos is on a dedicated Kubernetes node, and each mongo process hosting shards has its dedicated k8s node, while the config mong processes run a bit here and there where they happens to be deployed.</p>

<p>The cluster is used mainly for GridFS file hosting, with a typical file being around 100Mb.</p>

<p>I am doing stress tests with 1, 2 and 3 shards to see if it scales properly, and it doesn't.</p>

<p>If I start a brand new cluster with 2 shards and run my test it ingest files at (approx) twice the speed I had with 1 shard, but if I start the cluster with 1 shard, then perform the test, then add 1 more shard (total 2 shards), then perform the test again, the speed of ingestion is approx the same as before with 1 shard.</p>

<p>Looking at where chunks go, when I start the cluster immediately with 2 shards the load is evenly balanced between shards.
If I start with 1 shard and add a second after a some insertions, then the chunks tend to go all on the old shard and the balancer must bring them later to the second shard.</p>

<p>Quick facts:</p>

<ul>
<li><p>chunksize 1024 MB</p></li>
<li><p>sharding key is GridFS file_id, hashed</p></li>
</ul>

Federico Bonelli
<p>This is due to how hashed sharding and balancing works.</p>

<p>In an empty collection (from <a href="https://docs.mongodb.com/manual/core/hashed-sharding/#shard-an-empty-collection" rel="nofollow noreferrer">Shard an Empty Collection</a>):</p>

<blockquote>
  <p>The sharding operation creates empty chunks to cover the entire range of the shard key values and performs an initial chunk distribution. By default, the operation creates 2 chunks per shard and migrates across the cluster.</p>
</blockquote>

<p>So if you execute <code>sh.shardCollection()</code> on a cluster with <code>x</code> numbers of shards, it will create 2 chunks per shard and distribute them across the shards, totalling <code>2x</code> chunks across the cluster. Since the collection is empty, moving the chunks around take little time. Your ingestion will now be distributed evenly across the shards (assuming other things e.g. good cardinality of the hashed field).</p>

<p>Now if you add a new shard <em>after</em> the chunks were created, that shard starts empty and the balancer will start to send chunks to it using the <a href="https://docs.mongodb.com/manual/core/sharding-balancer-administration/index.html#migration-thresholds" rel="nofollow noreferrer">Migration Thresholds</a>. In a populated collection, this process may take a while to finish.</p>

<p>If while the balancer is still moving chunks around (which may not be empty now) you do another ingestion, the cluster is now doing two different jobs at the same time: 1) ingestion, and 2) balancing.</p>

<p>When you're doing this with 1 shard and add another shard, it's likely that the chunks you're ingesting into are still located in shard 1 and haven't moved to the new shard yet, so most data will go into that shard.</p>

<p>Thus you should wait until the cluster is balanced after adding the new shard before doing another ingestion. After it's balanced, the ingestion load should be more evenly distributed.</p>

<p><strong>Note:</strong> since your shard key is <code>file_id</code>, I'm assuming that each file is approximately the same size (~100 MB). If some files are much larger than others, some chunks will be busier than others as well.</p>
