ring

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2022 License: Apache-2.0 Imports: 4 Imported by: 0

README

ring

consistent hashing data structure


One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them...

The type ring implements consistent hashing data structure to orchestrate the topology without the global coordinator in the distributed environment. The library is developed after Erlang's consistent hashing data structure, which has been battle tested by author in various contexts.

Inspiration

The concept of consistent hashing has been developed in the past to deal with load-balancing in a dynamic environment. It solves the resizing problem of traditional mod-n hashing technique so that only the k/N fraction of keys needs to be reallocated when topology is modified, while the traditional hashing causes entire remapping of key space. The practical adoption of consistent hashing into distributed systems has shown a need for stricter balancing of keys, therefore hashing schema has been enhanced with the concept of virtual nodes. In this schema, each node claims randomly multiple tokens. The tokens of all nodes are placed on the ring according to their values. Every two consecutive tokens frame the arc, which is claimed by the corresponding node. Virtual node partitioning schema works well for load balancing of CPU bound workload but suffers for storage bounded workload due to the randomness in key ranges as it has been demonstrated by Amazon Dynamo. Virtual nodes do not guarantee determinism on key range reallocation when topology is modified nor predictability of anti-entropy processes.

The ultimate consistent hashing uses unrelated algorithms for partition, allocation and routing. The hash address space 2ᵐ-1 is divided into Q equally sized shards. Each node claims about Q/N shards with the help of T pseudo-randomly assigned tokens. The tokens are mapped into the hash address space to claim governance of shards to the node. When a node leaves the system, its shards are consistently distributed to the remaining nodes. Similarly, when a node joins the system it "steals" shards from nodes in the system in consistent ways. The request routing procedure maps the key into the hash space to determine the shard and corresponding node.

Installing

The latest version of the library is available at main branch. All development, including new features and bug fixes, take place on the main branch using forking and pull requests as described in contribution guidelines. The stable version is available via Golang modules.

  1. Use go get to retrieve the library and add it as dependency to your application.
go get -u github.com/fogfish/ring
  1. Import it in your code
import (
  "github.com/fogfish/ring"
)

Quick Example

Here is a minimal example on creating an instance of the ring, assembling topology from individual nodes and then routing the requests. See the full example

package main

import (
  "github.com/fogfish/ring"
)

func main() {
  /*
    create new ring instance with m=64, Q=8, T=8
  */
  ringo := ring.New(ring.WithM64(), ring.WithQ(8), ring.WithT(8))

  /*
    when all nodes join the topology is following

    ring: m=64, q=8, t=8
    |     [0, ffffffffffffffff]
    |     [ 18.54.73.101 113.181.90.103 102.190.90.78 140.93.207.103 92.106.122.149 ]
    |
    |     0: 1fffffffffffffff ⇒     1  ab26472ec2ed62a [18.54.73.101]
    |     1: 3fffffffffffffff ⇒     0 228ad527296bd2d5 [113.181.90.103]
    |     2: 5fffffffffffffff ⇒     2 5949b7cc2ac07642 [140.93.207.103]
    |     3: 7fffffffffffffff ⇒     3 6c13f457b56728ec [18.54.73.101]
    |     4: 9fffffffffffffff ⇒     0 931fb3cd1fc272eb [18.54.73.101]
    |     5: bfffffffffffffff ⇒     0 a22176d726c38cb5 [102.190.90.78]
    |     6: dfffffffffffffff ⇒     1 d613972f28795b25 [140.93.207.103]
    |     7: ffffffffffffffff ⇒     0 f27d0004a29a8dff [140.93.207.103]
  */
  ringo.Join("113.181.90.103")
  /* ... */
  ringo.Join("18.54.73.101")

  /*
    Lookup successor nodes for the key.
    It returns list of primary & handoff nodes

    Primary:
    1. {ffffffffffffffff | 0 - 140.93.207.103}
    2. {ffffffffffffffff | 1 - 18.54.73.101}
    3. {ffffffffffffffff | 0 - 113.181.90.103}

    Handoff:
    - empty
  */
  primary, handoff := ringo.SuccessorOf(3, "One ring to rule them all")

  // Handoff node and its shards to other
  ringo.Handoff("18.54.73.101")

  // Permanently leaves the topology
  ringo.Leave("18.54.73.101")
}

How To Contribute

The library is Apache 2.0 licensed and accepts contributions via GitHub pull requests:

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

The build and testing process requires Go latest version.

Build and run in your development console.

git clone https://github.com/fogfish/ring
cd ring
go test
go test -run=^$ -bench=. -cpu 1

commit message

The commit message helps us to write a good release note, speed-up review process. The message should address two question what changed and why. The project follows the template defined by chapter Contributing to a Project of Git book.

bugs

If you experience any issues with the library, please let us know via GitHub issues. We appreciate detailed and accurate reports that help us to identity and replicate the issue.

License

See LICENSE

Documentation

Overview

Copyright 2012 Dmitry Kolesnikov, All Rights Reserved

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

This section is empty.

Variables

View Source
var (
	M64_Q8_T8 = Options(
		WithM64(),
		WithQ(8),
		WithT(8),
		WithHash(sha1.New),
	)

	M64_Q4096_T256 = Options(
		WithM64(),
		WithQ(4096),
		WithT(256),
		WithHash(sha1.New),
	)
)

Functions

This section is empty.

Types

type Handoff

type Handoff Hashes

List of handoff hashes/nodes

type Hash

type Hash struct {
	// contains filtered or unexported fields
}

Hash value on the ring

func (Hash) Hash

func (hash Hash) Hash() uint64

func (Hash) Node

func (hash Hash) Node() string

func (Hash) Rank

func (hash Hash) Rank() int

func (Hash) String

func (hash Hash) String() string

type Hashes

type Hashes []Hash

List of Hashes

type Node

type Node interface {
	Hash() uint64
	Rank() int
	Node() string
}

Node representation

type Option

type Option func(ring *Ring)

Option for the ring structure

func Options

func Options(opts ...Option) Option

Options turns a list of Option instances into an Option.

func WithHash

func WithHash(f func() hash.Hash) Option

WithHash configures hashing algorithm for the ring

func WithM16

func WithM16() Option

WithM16 configures the ring param m=16, so that ring space is 2^m - 1

func WithM32

func WithM32() Option

WithM32 configures the ring param m=32, so that ring space is 2^m - 1

func WithM64

func WithM64() Option

WithM64 configures the ring param m=64, so that ring space is 2^m - 1

func WithM8

func WithM8() Option

WithM8 configures the ring param m=8, so that ring space is 2^m - 1

func WithQ

func WithQ(q uint64) Option

WithQ configures the ring param q, number of shards on the ring

func WithRing

func WithRing(r *Ring) Option

WithRing clones ring configuration into the new instance

func WithT

func WithT(n uint64) Option

WithT configures the ring param t, number of tokens to be claimed by the node

type Primary

type Primary Hashes

List of primary hashes/nodes

type Ring

type Ring struct {
	// contains filtered or unexported fields
}

Ring is consistent hashing data type.

func New

func New(opts ...Option) *Ring

New creates instances of the ring

func (*Ring) Address

func (ring *Ring) Address(key string) uint64

Address calculates address of key on the ring

func (*Ring) After

func (ring *Ring) After(n uint64, addr uint64) []Node

After returns list of N successors shards for the address.

func (*Ring) AfterKey

func (ring *Ring) AfterKey(n uint64, key string) []Node

AfterKey returns list of N successors shards for the key.

func (*Ring) Before

func (ring *Ring) Before(n uint64, addr uint64) []Node

Before returns list of N predecessors shards for the address.

func (*Ring) BeforeKey

func (ring *Ring) BeforeKey(n uint64, key string) []Node

BeforeKey returns list of N predecessors shards for the key.

func (*Ring) Debug

func (ring *Ring) Debug() string

Debug represents ring to string snapshot

func (*Ring) Handoff

func (ring *Ring) Handoff(node string) *Ring

Handoff node's responsibility.

func (*Ring) Has

func (ring *Ring) Has(node string) bool

Has return true if key exists in the ring

func (*Ring) Join

func (ring *Ring) Join(node string) *Ring

Join node to the ring. Node claims Q/N shards from the ring.

func (*Ring) Leave

func (ring *Ring) Leave(node string) *Ring

Leave node from the ring

func (*Ring) Lookup

func (ring *Ring) Lookup(addr uint64) Node

Lookup the address position on the ring

func (*Ring) LookupKey

func (ring *Ring) LookupKey(key string) Node

lookup the key position on the ring

func (*Ring) Members

func (ring *Ring) Members() []string

Members return list of nodes registered at ring

func (*Ring) Nodes

func (ring *Ring) Nodes() map[string][]Node

Nodes return list of nodes and its shards

func (*Ring) Shards

func (ring *Ring) Shards() []Node

Shards returns ring topology and its allocation

func (*Ring) Size

func (ring *Ring) Size() int

Size of ring, number of members joined the ring

func (*Ring) SuccessorOf

func (ring *Ring) SuccessorOf(n uint64, key string) (Primary, Handoff)

SuccessorOf return N distinct nodes to route key. The list of nodes is split to primary and handoff replicas.

For each node it returns the address of shard hit by the key, the node identity, the rank of node identity and its address on the ring.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL