Rendezvous or highest random weight (HRW) hashing [1] [2] is an algorithm that allows clients to achieve distributed agreement on a set of options out of a possible set of options. A typical application is when clients need to agree on which sites (or proxies) objects are assigned to.
Consistent hashing addresses the special case using a different method. Rendezvous hashing is both much simpler and more general than consistent hashing (see below).
Rendezvous hashing was invented by David Thaler and Chinya Ravishankar at the University of Michigan in 1996. [1] Consistent hashing appeared a year later in the literature.
Given its simplicity and generality, rendezvous hashing is now being preferred to consistent hashing in real-world applications. [3] [4] [5] Rendezvous hashing was used very early on in many applications including mobile caching, [6] router design, [7] secure key establishment, [8] and sharding and distributed databases. [9] Other examples of real-world systems that use Rendezvous Hashing include the Github load balancer, [10] the Apache Ignite distributed database, [11] the Tahoe-LAFS file store, [12] the CoBlitz large-file distribution service, [13] Apache Druid, [14] IBM's Cloud Object Store, [15] the Arvados Data Management System, [16] Apache Kafka, [17] and the Twitter EventBus pub/sub platform. [18]
One of the first applications of rendezvous hashing was to enable multicast clients on the Internet (in contexts such as the MBONE) to identify multicast rendezvous points in a distributed fashion. [19] [20] It was used in 1998 by Microsoft's Cache Array Routing Protocol (CARP) for distributed cache coordination and routing. [21] [22] Some Protocol Independent Multicast routing protocols use rendezvous hashing to pick a rendezvous point. [1]
Rendezvous hashing solves a general version of the distributed hash table problem: We are given a set of sites (servers or proxies, say). How can any set of clients, given an object , agree on a k-subset of sites to assign to ? The standard version of the problem uses k = 1. Each client is to make its selection independently, but all clients must end up picking the same subset of sites. This is non-trivial if we add a minimal disruption constraint, and require that when a site fails or is removed, only objects mapping to that site need be reassigned to other sites.
The basic idea is to give each site a score (a weight) for each object , and assign the object to the highest scoring site. All clients first agree on a hash function . For object , the site is defined to have weight . Each client independently computes these weights and picks the k sites that yield the k largest hash values. The clients have thereby achieved distributed -agreement.
If a site is added or removed, only the objects mapping to are remapped to different sites, satisfying the minimal disruption constraint above. The HRW assignment can be computed independently by any client, since it depends only on the identifiers for the set of sites and the object being assigned.
HRW easily accommodates different capacities among sites. If site has twice the capacity of the other sites, we simply represent twice in the list, say, as . Clearly, twice as many objects will now map to as to the other sites.
Consider the simple version of the problem, with k = 1, where all clients are to agree on a single site for an object O. Approaching the problem naively, it might appear sufficient to treat the n sites as buckets in a hash table and hash the object name O into this table. Unfortunately, if any of the sites fails or is unreachable, the hash table size changes, forcing all objects to be remapped. This massive disruption makes such direct hashing unworkable.
Under rendezvous hashing, however, clients handle site failures by picking the site that yields the next largest weight. Remapping is required only for objects currently mapped to the failed site, and disruption is minimal. [1] [2]
Rendezvous hashing has the following properties:
The standard version of Rendezvous Hashing described above works quite well for moderate n, but when is extremely large, the hierarchical use of Rendezvous Hashing achieves running time. [23] [24] [25] This approach creates a virtual hierarchical structure (called a "skeleton"), and achieves running time by applying HRW at each level while descending the hierarchy. The idea is to first choose some constant and organize the sites into clusters Next, build a virtual hierarchy by choosing a constant and imagining these clusters placed at the leaves of a tree of virtual nodes, each with fanout .
In the accompanying diagram, the cluster size is , and the skeleton fanout is . Assuming 108 sites (real nodes) for convenience, we get a three-tier virtual hierarchy. Since , each virtual node has a natural numbering in octal. Thus, the 27 virtual nodes at the lowest tier would be numbered in octal (we can, of course, vary the fanout at each level - in that case, each node will be identified with the corresponding mixed-radix number).
The easiest way to understand the virtual hierarchy is by starting at the top, and descending the virtual hierarchy. We successively apply Rendezvous Hashing to the set of virtual nodes at each level of the hierarchy, and descend the branch defined by the winning virtual node. We can in fact start at any level in the virtual hierarchy. Starting lower in the hierarchy requires more hashes, but may improve load distribution in the case of failures.
For example, instead of applying HRW to all 108 real nodes in the diagram, we can first apply HRW to the 27 lowest-tier virtual nodes, selecting one. We then apply HRW to the four real nodes in its cluster, and choose the winning site. We only need hashes, rather than 108. If we apply this method starting one level higher in the hierarchy, we would need hashes to get to the winning site. The figure shows how, if we proceed starting from the root of the skeleton, we may successively choose the virtual nodes , , and , and finally end up with site 74.
The virtual hierarchy need not be stored, but can be created on demand, since the virtual nodes names are simply prefixes of base- (or mixed-radix) representations. We can easily create appropriately sorted strings from the digits, as required. In the example, we would be working with the strings (at tier 1), (at tier 2), and (at tier 3). Clearly, has height , since and are both constants. The work done at each level is , since is a constant.
The value of can be chosen based on factors like the anticipated failure rate and the degree of desired load balancing. A higher value of leads to less load skew in the event of failure at the cost of higher search overhead.
The choice is equivalent to non-hierarchical rendezvous hashing. In practice, the hash function is very cheap, so can work quite well unless is very high.
For any given object, it is clear that each leaf-level cluster, and hence each of the sites, is chosen with equal probability.
One can enhance resiliency to failures by replicating each object O across the highest ranking r < m sites for O, choosing r based on the level of resiliency desired. The simplest strategy is to replicate only within the leaf-level cluster.
If the leaf-level site selected for O is unavailable, we select the next-ranked site for O within the same leaf-level cluster. If O has been replicated within the leaf-level cluster, we are sure to find O in the next available site in the ranked order of r sites. All objects that were held by the failed server appear in some other site in its cluster. (Another option is to go up one or more tiers in the skeleton and select an alternate from among the sibling virtual nodes at that tier. We then descend the hierarchy to the real nodes, as above.)
When a site is added to the system, it may become the winning site for some objects already assigned to other sites. Objects mapped to other clusters will never map to this new site, so we need to only consider objects held by other sites in its cluster. If the sites are caches, attempting to access an object mapped to the new site will result in a cache miss, the corresponding object will be fetched and cached, and operation returns to normal.
If sites are servers, some objects must be remapped to this newly added site. As before, objects mapped to other clusters will never map to this new site, so we need to only consider objects held by sites in its cluster. That is, we need only remap objects currently present in the m sites in this local cluster, rather than the entire set of objects in the system. New objects mapping to this site will of course be automatically assigned to it.
Because of its simplicity, lower overhead, and generality (it works for any k < n), rendezvous hashing is increasingly being preferred over consistent hashing. Recent examples of its use include the Github load balancer, [10] the Apache Ignite distributed database, [11] and by the Twitter EventBus pub/sub platform. [18]
Consistent hashing operates by mapping sites uniformly and randomly to points on a unit circle called tokens. Objects are also mapped to the unit circle and placed in the site whose token is the first encountered traveling clockwise from the object's location. When a site is removed, the objects it owns are transferred to the site owning the next token encountered moving clockwise. Provided each site is mapped to a large number (100–200, say) of tokens this will reassign objects in a relatively uniform fashion among the remaining sites.
If sites are mapped to points on the circle randomly by hashing 200 variants of the site ID, say, the assignment of any object requires storing or recalculating 200 hash values for each site. However, the tokens associated with a given site can be precomputed and stored in a sorted list, requiring only a single application of the hash function to the object, and a binary search to compute the assignment. Even with many tokens per site, however, the basic version of consistent hashing may not balance objects uniformly over sites, since when a site is removed each object assigned to it is distributed only over as many other sites as the site has tokens (say 100–200).
Variants of consistent hashing (such as Amazon's Dynamo) that use more complex logic to distribute tokens on the unit circle offer better load balancing than basic consistent hashing, reduce the overhead of adding new sites, and reduce metadata overhead and offer other benefits. [26]
Rendezvous hashing (HRW) is much simpler conceptually and in practice. It also distributes objects uniformly over all sites, given a uniform hash function. Unlike consistent hashing, HRW requires no precomputing or storage of tokens. Consider k =1. An object is placed into one of sites by computing the hash values and picking the site that yields the highest hash value. If a new site is added, new object placements or requests will compute hash values, and pick the largest of these. If an object already in the system at maps to this new site , it will be fetched afresh and cached at . All clients will henceforth obtain it from this site, and the old cached copy at will ultimately be replaced by the local cache management algorithm. If is taken offline, its objects will be remapped uniformly to the remaining sites.
Variants of the HRW algorithm, such as the use of a skeleton (see below), can reduce the time for object location to , at the cost of less global uniformity of placement. When is not too large, however, the placement cost of basic HRW is not likely to be a problem. HRW completely avoids all the overhead and complexity associated with correctly handling multiple tokens for each site and associated metadata.
Rendezvous hashing also has the great advantage that it provides simple solutions to other important problems, such as distributed -agreement.
Rendezvous hashing is both simpler and more general than consistent hashing. Consistent hashing can be shown to be a special case of HRW by an appropriate choice of a two-place hash function. From the site identifier the simplest version of consistent hashing computes a list of token positions, e.g., where hashes values to locations on the unit circle. Define the two place hash function to be where denotes the distance along the unit circle from to (since has some minimal non-zero value there is no problem translating this value to a unique integer in some bounded range). This will duplicate exactly the assignment produced by consistent hashing.
It is not possible, however, to reduce HRW to consistent hashing (assuming the number of tokens per site is bounded), since HRW potentially reassigns the objects from a removed site to an unbounded number of other sites.
In the standard implementation of rendezvous hashing, every node receives a statically equal proportion of the keys. This behavior, however, is undesirable when the nodes have different capacities for processing or holding their assigned keys. For example, if one of the nodes had twice the storage capacity as the others, it would be beneficial if the algorithm could take this into account such that this more powerful node would receive twice the number of keys as each of the others.
A straightforward mechanism to handle this case is to assign two virtual locations to this node, so that if either of that larger node's virtual locations has the highest hash, that node receives the key. But this strategy does not work when the relative weights are not integer multiples. For example, if one node had 42% more storage capacity, it would require adding many virtual nodes in different proportions, leading to greatly reduced performance. Several modifications to rendezvous hashing have been proposed to overcome this limitation.
The Cache Array Routing Protocol (CARP) is a 1998 IETF draft that describes a method for computing load factors which can be multiplied by each node's hash score to yield an arbitrary level of precision for weighting nodes differently. [21] However, one disadvantage of this approach is that when any node's weight is changed, or when any node is added or removed, all the load factors must be re-computed and relatively scaled. When the load factors change relative to one another, it triggers movement of keys between nodes whose weight was not changed, but whose load factor did change relative to other nodes in the system. This results in excess movement of keys. [27]
Controlled replication under scalable hashing or CRUSH [28] is an extension to RUSH [29] that improves upon rendezvous hashing by constructing a tree where a pseudo-random function (hash) is used to navigate down the tree to find which node is ultimately responsible for a given key. It permits perfect stability for adding nodes; however, it is not perfectly stable when removing or re-weighting nodes, with the excess movement of keys being proportional to the height of the tree.
The CRUSH algorithm is used by the ceph data storage system to map data objects to the nodes responsible for storing them. [30]
In 2005, Christian Schindelhauer and Gunnar Schomaker described a logarithmic method for re-weighting hash scores in a way that does not require relative scaling of load factors when a node's weight changes or when nodes are added or removed. [31] This enabled the dual benefits of perfect precision when weighting nodes, along with perfect stability, as only a minimum number of keys needed to be remapped to new nodes.
A similar logarithm-based hashing strategy is used to assign data to storage nodes in Cleversafe's data storage system, now IBM Cloud Object Storage. [27]
Rendezvous hashing is being used widely in real-world systems. A partial list includes Oracle's Database in-memory, [9] the GitHub load balancer, [10] the Apache Ignite distributed database, [11] the Tahoe-LAFS file store, [12] the CoBlitz large-file distribution service, [13] Apache Druid, [14] IBM's Cloud Object Store, [15] the Arvados Data Management System, [16] Apache Kafka, [17] and by the Twitter EventBus pub/sub platform. [18]
Implementation is straightforward once a hash function is chosen (the original work on the HRW method makes a hash function recommendation). [1] [2] Each client only needs to compute a hash value for each of the sites, and then pick the largest. This algorithm runs in time. If the hash function is efficient, the running time is not a problem unless is very large.
Python code implementing a weighted rendezvous hash: [27]
importmmh3importmathfromdataclassesimportdataclassfromtypingimportListdefhash_to_unit_interval(s:str)->float:"""Hashes a string onto the unit interval (0, 1]"""return(mmh3.hash128(s)+1)/2**128@dataclassclassNode:"""Class representing a node that is assigned keys as part of a weighted rendezvous hash."""name:strweight:floatdefcompute_weighted_score(self,key:str):score=hash_to_unit_interval(f"{self.name}: {key}")log_score=1.0/-math.log(score)returnself.weight*log_scoredefdetermine_responsible_node(nodes:list[Node],key:str):"""Determines which node of a set of nodes of various weights is responsible for the provided key."""returnmax(nodes,key=lambdanode:node.compute_weighted_score(key),default=None)
Example outputs of WRH:
>>> importwrh>>> node1=wrh.Node("node1",100)>>> node2=wrh.Node("node2",200)>>> node3=wrh.Node("node3",300)>>> str(wrh.determine_responsible_node([node1,node2,node3],"foo"))"Node(name='node1', weight=100)">>> str(wrh.determine_responsible_node([node1,node2,node3],"bar"))"Node(name='node2', weight=300)">>> str(wrh.determine_responsible_node([node1,node2,node3],"hello"))"Node(name='node2', weight=300)">>> nodes=[node1,node2,node3]>>> fromcollectionsimportCounter>>> responsible_nodes=[wrh.determine_responsible_node(... nodes,f"key: {key}").nameforkeyinrange(45_000)]>>> print(Counter(responsible_nodes))Counter({'node3': 22487, 'node2': 15020, 'node1': 7493})
In computing, a hash table is a data structure that implements an associative array, also called a dictionary or simply map; an associative array is an abstract data type that maps keys to values. A hash table uses a hash function to compute an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. During lookup, the key is hashed and the resulting hash indicates where the corresponding value is stored. A map implemented by a hash table is called a hash map.
In computer science, a trie, also called digital tree or prefix tree, is a type of search tree: specifically, a k-ary tree data structure used for locating specific keys from within a set. These keys are most often strings, with links between nodes defined not by the entire key, but by individual characters. In order to access a key, the trie is traversed depth-first, following the links between nodes, which represent each character in the key.
A distributed hash table (DHT) is a distributed system that provides a lookup service similar to a hash table. Key–value pairs are stored in a DHT, and any participating node can efficiently retrieve the value associated with a given key. The main advantage of a DHT is that nodes can be added or removed with minimum work around re-distributing keys. Keys are unique identifiers which map to particular values, which in turn can be anything from addresses, to documents, to arbitrary data. Responsibility for maintaining the mapping from keys to values is distributed among the nodes, in such a way that a change in the set of participants causes a minimal amount of disruption. This allows a DHT to scale to extremely large numbers of nodes and to handle continual node arrivals, departures, and failures.
In computer science, a perfect hash functionh for a set S is a hash function that maps distinct elements in S to a set of m integers, with no collisions. In mathematical terms, it is an injective function.
In computing, Chord is a protocol and algorithm for a peer-to-peer distributed hash table. A distributed hash table stores key-value pairs by assigning keys to different computers ; a node will store the values for all the keys for which it is responsible. Chord specifies how keys are assigned to nodes, and how a node can discover the value for a given key by first locating the node responsible for that key.
Kademlia is a distributed hash table for decentralized peer-to-peer computer networks designed by Petar Maymounkov and David Mazières in 2002. It specifies the structure of the network and the exchange of information through node lookups. Kademlia nodes communicate among themselves using UDP. A virtual or overlay network is formed by the participant nodes. Each node is identified by a number or node ID. The node ID serves not only as identification, but the Kademlia algorithm uses the node ID to locate values.
A Bloom filter is a space-efficient probabilistic data structure, conceived by Burton Howard Bloom in 1970, that is used to test whether an element is a member of a set. False positive matches are possible, but false negatives are not – in other words, a query returns either "possibly in set" or "definitely not in set". Elements can be added to the set, but not removed ; the more items added, the larger the probability of false positives.
In computing, a persistent data structure or not ephemeral data structure is a data structure that always preserves the previous version of itself when it is modified. Such data structures are effectively immutable, as their operations do not (visibly) update the structure in-place, but instead always yield a new updated structure. The term was introduced in Driscoll, Sarnak, Sleator, and Tarjan's 1986 article.
Lustre is a type of parallel distributed file system, generally used for large-scale cluster computing. The name Lustre is a portmanteau word derived from Linux and cluster. Lustre file system software is available under the GNU General Public License and provides high performance file systems for computer clusters ranging in size from small workgroup clusters to large-scale, multi-site systems. Since June 2005, Lustre has consistently been used by at least half of the top ten, and more than 60 of the top 100 fastest supercomputers in the world, including the world's No. 1 ranked TOP500 supercomputer in November 2022, Frontier, as well as previous top supercomputers such as Fugaku, Titan and Sequoia.
In computer science, consistent hashing is a special kind of hashing technique such that when a hash table is resized, only keys need to be remapped on average where is the number of keys and is the number of slots. In contrast, in most traditional hash tables, a change in the number of array slots causes nearly all keys to be remapped because the mapping between the keys and the slots is defined by a modular operation.
Linear hashing (LH) is a dynamic data structure which implements a hash table and grows or shrinks one bucket at a time. It was invented by Witold Litwin in 1980. It has been analyzed by Baeza-Yates and Soza-Pollman. It is the first in a number of schemes known as dynamic hashing such as Larson's Linear Hashing with Partial Extensions, Linear Hashing with Priority Splitting, Linear Hashing with Partial Expansions and Priority Splitting, or Recursive Linear Hashing.
Cuckoo hashing is a scheme in computer programming for resolving hash collisions of values of hash functions in a table, with worst-case constant lookup time. The name derives from the behavior of some species of cuckoo, where the cuckoo chick pushes the other eggs or young out of the nest when it hatches in a variation of the behavior referred to as brood parasitism; analogously, inserting a new key into a cuckoo hashing table may push an older key to a different location in the table.
BitVault is a content-addressable distributed storage system, developed by Microsoft Research in China. BitVault uses peer-to-peer technology to distribute the tasks of storing and managing data. As such, there is no central authority responsible for management of the system. Rather, it is self-managing, provides high availability, reliability and scales up in a self-organizing manner, with low administrative overhead, which is almost constant irrespective of the size of the distributed overlay network.
A hash chain is the successive application of a cryptographic hash function to a piece of data. In computer security, a hash chain is a method used to produce many one-time keys from a single key or password. For non-repudiation, a hash function can be applied successively to additional pieces of data in order to record the chronology of data's existence.
Magma is a distributed file system based on a distributed hash table, written in C, compatible with Linux and BSD kernels using FUSE.
In the BitTorrent file distribution system, a torrent file or meta-info file is a computer file that contains metadata about files and folders to be distributed, and usually also a list of the network locations of trackers, which are computers that help participants in the system find each other and form efficient distribution groups called swarms. Torrent files are normally named with the extension .torrent
.
Guided tour puzzle (GTP) protocol is a cryptographic protocol for mitigating application layer denial of service attacks. It aims to overcome the shortcoming of computation-based puzzle protocols, in which clients are required to compute hard CPU or memory-bound puzzles that favor clients with abundant computational resources. Guided tour puzzle protocol can be seen as a form of proof-of-work (POW) protocol.
A distributed file system for cloud is a file system that allows many clients to have access to data and supports operations on that data. Each data file may be partitioned into several parts called chunks. Each chunk may be stored on different remote machines, facilitating the parallel execution of applications. Typically, data is stored in files in a hierarchical tree, where the nodes represent directories. There are several ways to share files in a distributed architecture: each solution must be suitable for a certain type of application, depending on how complex the application is. Meanwhile, the security of the system must be ensured. Confidentiality, availability and integrity are the main keys for a secure system.
Elliptics is a distributed key–value data storage with open source code. By default it is a classic distributed hash table (DHT) with multiple replicas put in different groups. Elliptics was created to meet requirements of multi-datacenter and physically distributed storage locations when storing huge amount of medium and large files.
Approximate membership query filters comprise a group of space-efficient probabilistic data structures that support approximate membership queries. An approximate membership query answers whether an element is in a set or not with a false positive rate of .
{{cite journal}}
: Cite journal requires |journal=
(help)