{"id":789,"date":"2020-07-18T16:09:09","date_gmt":"2020-07-18T23:09:09","guid":{"rendered":"https:\/\/finaldie.com\/blog\/?p=789"},"modified":"2020-07-20T14:08:13","modified_gmt":"2020-07-20T21:08:13","slug":"data-partitioning-consistent-hashing","status":"publish","type":"post","link":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/","title":{"rendered":"Data partitioning: Consistent-Hashing"},"content":{"rendered":"<h2>Overview<\/h2>\n<p>From the previous <a href=\"https:\/\/finaldie.com\/blog\/load-balancer-will-our-system-survive\/\">article<\/a> we may already have a basic concept of the load balancer, this time, let&#8217;s look at one of the popular algorithm: <strong>Consistent Hashing<\/strong><\/p>\n<h2>Use Cases<\/h2>\n<p>Consistent Hashing is quite useful when dealing with the cache distributed issue in a dynamic environment (The servers keep adding\/removing) compares with the <code>Mod-Hashing<\/code>.<\/p>\n<p>Basically, it maps keys and values into the same hash ring circle, it can be implemented via a <code>hash-function<\/code> + <code>binary-search<\/code>. Additionally, we will bring the <code>virtual nodes<\/code> to overcome the unbalancing issue when the servers be added or removed.<\/p>\n<table>\n<thead>\n<tr>\n<th>Case<\/th>\n<th>Products<\/th>\n<th>Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Auto data partition<\/td>\n<td>Couchbase<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td>OpenStack\\&#8217;s Object Storage<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td>Dynamo<\/td>\n<td>Amazon\\&#8217;s K-V storage<\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td>Apache Cassnadra<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Distributed Cache<\/td>\n<td>Akka\\&#8217;s router<\/td>\n<td><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>The Motivation<\/h2>\n<p>If we have <code>4<\/code> <strong>cache<\/strong> servers (index: [0,1,2,3]), the different server maintains different cache data and the routing formula is the basic <strong>Mod-Hashing<\/strong>: <code>p = h(x) % 4<\/code>. Then, if the server <code>2<\/code> is down, according to the formula we will have: <code>p = h(x) % 3<\/code>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/image-1595107439835.png\" alt=\"file\" \/><\/p>\n<p>From above, we can clearly see that, if the server 2 is down, all requests will be re-routed to a different server, it will cause the cache on the server are all invalid.<\/p>\n<p>Basically we want to minimize the impact of the cache hit rate when add\/remove nodes.<\/p>\n<p>Follow up:<\/p>\n<ul>\n<li>What if server 1, 3 or 4 is down?<\/li>\n<\/ul>\n<p>Let&#8217;s see:<\/p>\n<ul>\n<li>Server 1 down? 4 requests are re-routed.<\/li>\n<li>Server 2 down? 4 requests are re-routed.<\/li>\n<li>Server 3 down? 3 requests are re-routed. (req [2,3,4])<\/li>\n<li>Server 4 down? 2 requests are re-routed. (req [3,4])<\/li>\n<\/ul>\n<p>Now, we have a formula to calculate how many nodes be impacted: <code class=\"katex-inline\">n = T - \\max(ServerIdx - 1, 0)<\/code><\/p>\n<p>Where:<\/p>\n<ul>\n<li><code>n<\/code>: number of nodes impacted<\/li>\n<li><code>T<\/code>: Total number of servers<\/li>\n<li><code>ServerIdx<\/code>: The server at ServerIdx is down<\/li>\n<\/ul>\n<p>In order to solve the problem, the consistent-hashing comes to the picture, with:<\/p>\n<ul>\n<li>Add a node: <code>1 \/ n + 1<\/code> data will be moved to the new node<\/li>\n<li>Remove a node: <code>1 \/ n<\/code> data will be redistributed to all other nodes<\/li>\n<\/ul>\n<h2>How it works<\/h2>\n<p>The idea is quite simple, <strong>hash the requests and server nodes in the same hash ring circle<\/strong>, then whenever the server nodes changes, the most of the requests will remain the same routing path, only a very small part of requests will be remapped, which is friendly to the cache likely service.<\/p>\n<p>Let&#8217;s say we only have two nodes <code>a<\/code> and <code>b<\/code>, for each node, we hash its unique nodeId then get hashcodes <code>ha = 100<\/code> and <code>hb = 200<\/code>, now we put it into a circle range [0, 255], then if any request incoming, we apply the same hash function to get a hashcode <code>r1 = 150<\/code>, by having this hashcode, we find which node is the <strong>next closest node in the clockwise order<\/strong>, in this case, the next one is node <code>b<\/code> due to its hashcode is <code>200<\/code>.<\/p>\n<p>Then if we add a new node <code>c<\/code> (hashcode is <code>150<\/code>), then only those requests are hashed in the range [101, 150] will be mapped to the node <code>c<\/code>, all other requests will be remain the same path.<\/p>\n<h3>Checklist<\/h3>\n<p>Once we decide to use it, we can review and define the thresholds from below checklist first:<\/p>\n<ol>\n<li>Replicas per node (1:1, 2:1, &#8230;, or 10:1), details in the <a href=\"#virtual-node\">Virtual-Node<\/a> section<\/li>\n<li>Data range (e.g. [0, 256] or [0, 2^32], &#8230;)<\/li>\n<li>Hash function<\/li>\n<li>Hash dimension\n<ul>\n<li>For queries: requestId, userId, &#8230;<\/li>\n<li>For nodes: unique nodeId\/nodeName + replicaId<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h3>Example Case<\/h3>\n<p>We have 3 nodes(no replicas), and we define:<\/p>\n<ul>\n<li>Replicas per node: 0<\/li>\n<li>Data range: 256<\/li>\n<li>Hash function: uniform hash<\/li>\n<li>Hash Dimension:\n<ul>\n<li>For queries: requestId<\/li>\n<li>For nodes: unique nodeId + replicaId (Here we don&#8217;t have)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Then we <code class=\"katex-inline\">hash<\/code> each node on to the circle, assume we have:<\/p>\n<table>\n<thead>\n<tr>\n<th>Node<\/th>\n<th>NodeId<\/th>\n<th>Hash Value<\/th>\n<th>Routing Range<\/th>\n<th>Routing Weight<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>A<\/td>\n<td>1<\/td>\n<td>30<\/td>\n<td>148 &#8211; 255, 0 &#8211; 30<\/td>\n<td>139<\/td>\n<\/tr>\n<tr>\n<td>B<\/td>\n<td>2<\/td>\n<td>64<\/td>\n<td>31 &#8211; 64<\/td>\n<td>34<\/td>\n<\/tr>\n<tr>\n<td>C<\/td>\n<td>3<\/td>\n<td>147<\/td>\n<td>65 &#8211; 147<\/td>\n<td>83<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now initially, we have a hash ring circle like below:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/image-1595110710900.png\" alt=\"file\" \/><\/p>\n<h3>Add Node<\/h3>\n<p>Let&#8217;s add one more node <strong>D<\/strong> into the system:<\/p>\n<table>\n<thead>\n<tr>\n<th>Name<\/th>\n<th>NodeId<\/th>\n<th>Hash Value<\/th>\n<th>Routing Range<\/th>\n<th>Routing Weight<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>A<\/td>\n<td>1<\/td>\n<td>30<\/td>\n<td>202 &#8211; 255, 0 &#8211; 30<\/td>\n<td>85<\/td>\n<\/tr>\n<tr>\n<td>B<\/td>\n<td>2<\/td>\n<td>64<\/td>\n<td>31 &#8211; 64<\/td>\n<td>34<\/td>\n<\/tr>\n<tr>\n<td>C<\/td>\n<td>3<\/td>\n<td>147<\/td>\n<td>65 &#8211; 147<\/td>\n<td>83<\/td>\n<\/tr>\n<tr>\n<td>D<\/td>\n<td>4<\/td>\n<td>201<\/td>\n<td>148 &#8211; 201<\/td>\n<td>54<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th>Time Complexity<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>No Replica<\/td>\n<td>O(logN)<\/td>\n<\/tr>\n<tr>\n<td>R Replica(s)<\/td>\n<td>O(RlogNR)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>After adding a new node, now the hash ring becomes:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/image-1595110757280.png\" alt=\"file\" \/><\/p>\n<h3>Find Node<\/h3>\n<p>For a request, for example, we use <code>requestId<\/code> as the dimension we want to hash:<\/p>\n<ol>\n<li>Take it into the hash function <code>f(x)<\/code>, get a hash value <code>y<\/code><\/li>\n<li>Take <code>y<\/code> to do a binary search on the circle range to find the <code>lower-bound<\/code> of node <code>Ki<\/code><\/li>\n<li>Query the key-node mapping to get the actual node (here since we don&#8217;t have replica, so the <code>Ki<\/code> is the node)<\/li>\n<\/ol>\n<p><strong>Examples:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Hash-Value mod 256<\/th>\n<th>Route-To<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>10<\/td>\n<td>A<\/td>\n<\/tr>\n<tr>\n<td>200<\/td>\n<td>A<\/td>\n<\/tr>\n<tr>\n<td>40<\/td>\n<td>B<\/td>\n<\/tr>\n<tr>\n<td>100<\/td>\n<td>C<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Complexity:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th>Time Complexity<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>No Replica<\/td>\n<td>O(logN)<\/td>\n<\/tr>\n<tr>\n<td>R Replica(s)<\/td>\n<td>O(logNR)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Where:<\/p>\n<ul>\n<li><code>N<\/code>: number of the nodes<\/li>\n<li><code>R<\/code>: number of the replicas per node<\/li>\n<\/ul>\n<h3>Remove Node<\/h3>\n<p>Let&#8217;s remove a node <strong>C<\/strong> from the system:<\/p>\n<table>\n<thead>\n<tr>\n<th>Name<\/th>\n<th>NodeId<\/th>\n<th>Hash Value<\/th>\n<th>Routing Range<\/th>\n<th>Routing Weight<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>A<\/td>\n<td>1<\/td>\n<td>30<\/td>\n<td>202 &#8211; 255, 0 &#8211; 30<\/td>\n<td>85<\/td>\n<\/tr>\n<tr>\n<td>B<\/td>\n<td>2<\/td>\n<td>64<\/td>\n<td>31 &#8211; 64<\/td>\n<td>34<\/td>\n<\/tr>\n<tr>\n<td>~~C~~<\/td>\n<td>~~3~~<\/td>\n<td>~~147~~<\/td>\n<td>n\/a<\/td>\n<td>n\/a<\/td>\n<\/tr>\n<tr>\n<td>D<\/td>\n<td>4<\/td>\n<td>201<\/td>\n<td>65 &#8211; 201<\/td>\n<td>137<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th>Time Complexity<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>No Replica<\/td>\n<td>O(logN)<\/td>\n<\/tr>\n<tr>\n<td><code>R<\/code> Replica<\/td>\n<td>O(RlogNR)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>After removal, the hash ring circle looks like below:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/image-1595110878860.png\" alt=\"file\" \/><\/p>\n<h3>Virtual Node<\/h3>\n<p>Still, let&#8217;s say we have 4 nodes: <code class=\"katex-inline\">n_a<\/code>, <code class=\"katex-inline\">n_b<\/code>, <code class=\"katex-inline\">n_c<\/code>, <code class=\"katex-inline\">n_d<\/code>, let&#8217;s assume if the replica:node is the<code>1:1<\/code>, based on the algorithm above: If we remove a node <code class=\"katex-inline\">n_b<\/code> , then all previous requests which mapped to <code class=\"katex-inline\">n_b<\/code> will be remapped to the next one <code class=\"katex-inline\">n_c<\/code>, so <code class=\"katex-inline\">n_c<\/code> now is taking <strong>2x<\/strong> load than others, but all others are still the same load.<\/p>\n<p>In the worst case, the <code class=\"katex-inline\">n_c<\/code> cannot handle the <strong>2x<\/strong> load, then it&#8217;s down quickly, so the next node <code class=\"katex-inline\">n_d<\/code> will taking <strong>3x<\/strong> load, which expected to be down soon as well. (We assume all the hardwares are the same for the 4 nodes)<\/p>\n<h4>Snow Crash<\/h4>\n<p>To deal with the <strong>unbalanced load<\/strong> or avoid <strong>Snow Crash<\/strong>, people introduced <strong>virtual node<\/strong> into the consistent hashing algorithm.<\/p>\n<p>Instead of keep key:node is always <code>1:1<\/code>, We assign multiple keys per node, either fixed or dynamic ratio:<\/p>\n<ul>\n<li>A fixed <code>n:1<\/code> ratio: Like 3 keys per node<\/li>\n<li>A dynamic ratio: node1 : [r1, r2, r3], node2 : [r4, r5]<\/li>\n<\/ul>\n<p>So that, every time when we:<\/p>\n<ul>\n<li><strong>Add a node:<\/strong> Add multiple keys into the hash ring circle, which mixed into the current keys evenly.<\/li>\n<li><strong>Remove a node:<\/strong> Remove the corresponding keys from the hash ring circle.<\/li>\n<\/ul>\n<p>A practical number of the virtual node is a number in the range of <strong>[100, 300]<\/strong><\/p>\n<p>Like this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/image-1595111115842.png\" alt=\"file\" \/><\/p>\n<p>The method to add more virtual nodes are quite straight-forward, one example<br \/>\nis, we took <code>NodeId_ReplicaId<\/code> string into the hash function <code class=\"katex-inline\">f{x}<\/code>, then the<br \/>\nhash value <code class=\"katex-inline\">y<\/code> will be the hash code for this node-replica.<\/p>\n<h4>Complexity<\/h4>\n<table>\n<thead>\n<tr>\n<th>Action<\/th>\n<th>Time Complexity<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Add a node<\/td>\n<td>O(Rlog(RN))<\/td>\n<\/tr>\n<tr>\n<td>Rmove a node<\/td>\n<td>O(Rlog(RN))<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Where:<\/p>\n<ul>\n<li><code>R<\/code>: Number of replicas per node<\/li>\n<li><code>N<\/code>: Number of nodes<\/li>\n<\/ul>\n<h4>Tweak The Replicas<\/h4>\n<p>Even with virtual nodes(replicas) we may still face the unbalancing issue, ideally, the replicas number in [100, 300] could be a fit for most of the product, but it&#8217;s still preferred to do an offline analysis and an online A\/B test according to the different product, then we can choose the right number before releasing it into the production.<\/p>\n<p>In order to have a quick sense, there is a <a href=\"https:\/\/www.youtube.com\/watch?v=Jb1UFTXcwnU\">Video<\/a> I made, we can see our cluster performs good or bad in the 3 cases (replicas = 200, 400, 600).<\/p>\n<h2>Implementation<\/h2>\n<p>Below is a simple example code to show the basic idea.<\/p>\n<h3>C++<\/h3>\n<pre><code class=\"language-cpp\">#define HASH_RING_SZ 256\n\nstruct Node {\n  int id_;\n  int repl_;\n  int hashCode_;\n  Node(int id, int replica) : id_(id), repl_(replica) {\n    hashCode_ =\n      hash&lt;string&gt;{} (to_string(id_) + \"_\" + to_string(repl_)) % HASH_RING_SZ;\n  }\n};\n\nclass ConsistentHashing {\nprivate:\n  unordered_map&lt;int\/*nodeId*\/, vector&lt;Node*&gt;&gt; id2node;\n  map&lt;int\/*hash code*\/, Node*&gt; ring;\n\npublic:\n  ConsistentHashing() {}\n\n  \/\/ Allow dynamically assign the node replicas\n  \/\/ O(Rlog(RN)) time\n  void addNode(int id, int replica) {\n    for (int i = 0; i &lt; replica; i++) {\n      Node* repl = new Node(id, replica);\n\n      id2node[id].push_back(repl);\n      ring.insert({node-&gt;hashCode_, repl});\n    }\n  }\n\n  \/\/ O(Rlog(RN)) time\n  void removeNode(int id) {\n    auto repls = id2node[id];\n    if (repls.empty()) return;\n\n    for (auto* repl : repls) {\n      ring.erase(repl-&gt;hashCode_);\n    }\n\n    id2node.erase(id);\n  }\n\n  \/\/ Return the nodeId\n  \/\/ O(log(RN)) time\n  int findNode(const string&amp; key) {\n    int h = hash&lt;string&gt;{}(key) % HASH_RING_SZ;\n\n    auto it = ring.lower_bound(h);\n    if (it == ring.end()) it == ring.begin();\n\n    return it-&gt;second-&gt;id;\n  }\n};\n<\/code><\/pre>\n<h2>References<\/h2>\n<ul>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Consistent_hashing\">Consistent Hashing &#8211; Wikipedia<\/a><\/li>\n<li><a href=\"http:\/\/michaelnielsen.org\/blog\/consistent-hashing\/\">Consistent Hashing by Michael Nielsen on June 3, 2009<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Overview From the previous article we may already have a basic concept of the load balancer, this time, let&#8217;s look at one of the popular algorithm: Consistent Hashing Use Cases Consistent Hashing is quite useful when dealing with the cache distributed issue in a dynamic environment (The servers keep adding\/removing) compares with the Mod-Hashing. Basically, &#8230; <a title=\"Data partitioning: Consistent-Hashing\" class=\"read-more\" href=\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/\" aria-label=\"More on Data partitioning: Consistent-Hashing\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":810,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[50,49],"tags":[54,53],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Data partitioning: Consistent-Hashing - Final Blog<\/title>\n<meta name=\"description\" content=\"A post for quick understand the concepts along with the code snippets\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Data partitioning: Consistent-Hashing - Final Blog\" \/>\n<meta property=\"og:description\" content=\"A post for quick understand the concepts along with the code snippets\" \/>\n<meta property=\"og:url\" content=\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/\" \/>\n<meta property=\"og:site_name\" content=\"Final Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/hu.yuzhang\" \/>\n<meta property=\"article:published_time\" content=\"2020-07-18T23:09:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-07-20T21:08:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1440\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"final\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@hyzwowtools\" \/>\n<meta name=\"twitter:site\" content=\"@hyzwowtools\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"final\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/\",\"url\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/\",\"name\":\"Data partitioning: Consistent-Hashing - Final Blog\",\"isPartOf\":{\"@id\":\"https:\/\/finaldie.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg\",\"datePublished\":\"2020-07-18T23:09:09+00:00\",\"dateModified\":\"2020-07-20T21:08:13+00:00\",\"author\":{\"@id\":\"https:\/\/finaldie.com\/blog\/#\/schema\/person\/2d4c840d6e8e197f8ade98af2bd2fab3\"},\"description\":\"A post for quick understand the concepts along with the code snippets\",\"breadcrumb\":{\"@id\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#primaryimage\",\"url\":\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg\",\"contentUrl\":\"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg\",\"width\":2560,\"height\":1440},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/finaldie.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Data partitioning: Consistent-Hashing\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/finaldie.com\/blog\/#website\",\"url\":\"https:\/\/finaldie.com\/blog\/\",\"name\":\"Final Blog\",\"description\":\"As simple as possible...\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/finaldie.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/finaldie.com\/blog\/#\/schema\/person\/2d4c840d6e8e197f8ade98af2bd2fab3\",\"name\":\"final\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/finaldie.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/4c720545b79ddb0f23b527e0bbcfd9bc?s=96&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/4c720545b79ddb0f23b527e0bbcfd9bc?s=96&r=g\",\"caption\":\"final\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Data partitioning: Consistent-Hashing - Final Blog","description":"A post for quick understand the concepts along with the code snippets","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/","og_locale":"en_US","og_type":"article","og_title":"Data partitioning: Consistent-Hashing - Final Blog","og_description":"A post for quick understand the concepts along with the code snippets","og_url":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/","og_site_name":"Final Blog","article_publisher":"https:\/\/www.facebook.com\/hu.yuzhang","article_published_time":"2020-07-18T23:09:09+00:00","article_modified_time":"2020-07-20T21:08:13+00:00","og_image":[{"width":2560,"height":1440,"url":"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg","type":"image\/jpeg"}],"author":"final","twitter_card":"summary_large_image","twitter_creator":"@hyzwowtools","twitter_site":"@hyzwowtools","twitter_misc":{"Written by":"final","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/","url":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/","name":"Data partitioning: Consistent-Hashing - Final Blog","isPartOf":{"@id":"https:\/\/finaldie.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#primaryimage"},"image":{"@id":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#primaryimage"},"thumbnailUrl":"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg","datePublished":"2020-07-18T23:09:09+00:00","dateModified":"2020-07-20T21:08:13+00:00","author":{"@id":"https:\/\/finaldie.com\/blog\/#\/schema\/person\/2d4c840d6e8e197f8ade98af2bd2fab3"},"description":"A post for quick understand the concepts along with the code snippets","breadcrumb":{"@id":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#primaryimage","url":"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg","contentUrl":"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg","width":2560,"height":1440},{"@type":"BreadcrumbList","@id":"https:\/\/finaldie.com\/blog\/data-partitioning-consistent-hashing\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/finaldie.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Data partitioning: Consistent-Hashing"}]},{"@type":"WebSite","@id":"https:\/\/finaldie.com\/blog\/#website","url":"https:\/\/finaldie.com\/blog\/","name":"Final Blog","description":"As simple as possible...","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/finaldie.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/finaldie.com\/blog\/#\/schema\/person\/2d4c840d6e8e197f8ade98af2bd2fab3","name":"final","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/finaldie.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/4c720545b79ddb0f23b527e0bbcfd9bc?s=96&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/4c720545b79ddb0f23b527e0bbcfd9bc?s=96&r=g","caption":"final"}}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/finaldie.com\/blog\/wp-content\/uploads\/2020\/07\/lb_consistent_hashing_cover-scaled.jpg","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/posts\/789"}],"collection":[{"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/comments?post=789"}],"version-history":[{"count":6,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/posts\/789\/revisions"}],"predecessor-version":[{"id":809,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/posts\/789\/revisions\/809"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/media\/810"}],"wp:attachment":[{"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/media?parent=789"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/categories?post=789"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/finaldie.com\/blog\/wp-json\/wp\/v2\/tags?post=789"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}