ring
Consistent hashing with virtual nodes
What to watch
Which keys move when a fifth node joins
Seed: 1 (default). The program asks a question at the interesting moment; with Predict on, the run stops there so you can answer before you see what happens.
Run it
Edit any file and press Run. The tabs hold the program and the packages it uses.
# package main
use github.com/synodlang/examples/ring
use github.com/synodlang/examples/ringlook
use std/lesson
use std/lists
use std/sim
define main
set nodes to (sim.Cluster of 4 running ring)
set keys to {"user:1", "user:2", "user:3", "cart:7", "cart:9", "order:12"}
each k in keys
emit (message ring.Placed with Key k, Owner (item 0 of (ring.Owners of k among nodes count 1))) by sim.Nobody
after 2s
lesson.Ask "A fifth node joins the ring. How many keys move?" choosing {"All six: every key is hashed again", "None: the new node starts empty", "Only the keys the new node takes over: one of the six", "Half of them"}
set grown to (lists.Append (sim.Spawn ring) to nodes)
each k in keys
emit (message ring.Placed with Key k, Owner (item 0 of (ring.Owners of k among grown count 1))) by sim.Nobody
sim.StopAt 4s
# package ring
use std/lists
use std/maps
use std/math
use std/sim
use std/text
Placed has
set Key to ""
set Owner to sim.Nobody
define Vnodes
result 8
# Each node owns several points on a ring of [0, 1), derived from its name
define TokensOf [n: node]
set tokens to {}
each i in (lists.Range 1 to Vnodes)
set tokens to (lists.Append (math.Hash (text.Join {(sim.Name n), "#", (text.From i)})) to tokens)
result tokens
# Clockwise distance from a key's point to the nearest point of a node
define Distance from [h: number] to [n: node]
set best to 1
each t in (TokensOf n)
set d to (math.Remainder of (t minus h) by 1)
if (d less than best)
set best to d
result best
# The first r nodes clockwise from the key own it
define Owners of [k: text] among [ns: list of node] count [r: number]
set h to (math.Hash k)
set distances to {}
each n in ns
set item n of distances to (Distance from h to n)
result (lists.Take r from (maps.KeysByValue distances))
# package ringlook
use github.com/synodlang/examples/ring
use std/look
use std/sim
use std/theme
when sim.Started
draw (look.Circle 300) as wheel
look.Fill wheel with theme.Paper
look.Stroke wheel with theme.Line
# A node sits at its first token; small marks show all its tokens
when (sim.Added n)
draw (look.Card for n) as card for n
look.Orbit card around look.Center at (item 0 of (ring.TokensOf n))
each t in (ring.TokensOf n)
draw (look.Circle 4) as mark
look.Fill mark with theme.Quiet
look.Orbit mark around look.Center at t
# A key label sits beside its owner and morphs when the owner changes
when (sim.Emitted ring.Placed by origin)
draw (look.Label ring.Placed.Key) as tag for ring.Placed.Key
look.Place tag beside (card of ring.Placed.Owner)
On your own machine: synod run ./ring. Source on GitHub · all examples