Tapestry Gearup
CS1380: Distributed Systems S22
By: March & Arvind
Slides link available on Website
Motivations
System Overview
Key-value pair
<K, V>
Node
1
Node
2
Node
3
Node
X
Node
Y
Well-known Root of K
Node
Z
Where is K?
K is here!
K is here!
...
Node 3 tells me it has K.
Node storing <K,V>
FindRoot(k)
Publish(k)
Data Structure Overview
Node A
Node B
| | |
| Node B | |
| | |
|
Node A |
|
Finding Root: FindRoot(id ID, level int32)
Finds root node of key (via FindRootRPC), also returns a set of nodes to remove
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
Check local Routing Table for nextHop
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
Check local Routing Table for nextHop
12
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
Check local Routing Table for nextHop
12
nextHop
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
FindRootRPC to nextHop
12
nextHop
tap2.FindRootRPC(12, 1)
FindRoot(12, 1)
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
Check local Routing Table for nextHop
12
nextHop
tap2.FindRootRPC(12, 1)
FindRoot(12, 1)
12
nextHop
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
FindRootRPC to nextHop
12
nextHop
tap2.FindRootRPC(12, 1)
FindRoot(12, 1)
12
nextHop
tap3.FindRootRPC(12, 2)
FindRoot(12, 2)
2 >= DIGITS → ROOT!
Finding Root: FindRoot(id ID, level int32)
Local Node
Tap 1 (ID: 01)
Tap 2 (ID: 21)
Tap 3 (ID: 22)
FindRoot(12, 0)
| 0 | 1 | 2 |
xx | 01 | | 21 |
0x | | 01 | |
| 0 | 1 | 2 |
xx | 01 | | 21 |
2x | | 21 | 22 |
| 0 | 1 | 2 |
xx | 01 | | 22 |
2x | | 21 | 22 |
Return root
12
nextHop
tap2.FindRootRPC(12, 1)
FindRoot(12, 1)
12
nextHop
tap3.FindRootRPC(12, 2)
FindRoot(12, 2)
2 >= DIGITS → ROOT!
Root = tap3 (ID: 22)
Root = tap3 (ID: 22)
Storing Object: Store(key string, value []byte)
Storing Object: Store(key string, value []byte)
Storing Object: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
Tapestry CLI: Store(key string, value []byte)
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
1. Publish Key
publish(key)
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
2. Find root of key
publish(key)
findRootOnRemoteNode(local.node, key)
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
3. FindRootRPC
publish(key)
findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
3. FindRootRPC
publish(key)
findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
FindRootRPC
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
3. FindRootRPC
publish(key)
findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
FindRootRPC
FindNextHop?
FindNextHop?�
Nope.�We are the root!
Register key to LocationMap
FindRootRPC
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
3. FindRootRPC
publish(key)
root := findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
FindRootRPC
FindNextHop?
FindNextHop?�
Nope.�We are the root!
Register key to LocationMap
FindRootRPC
Root
Root
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
4. RegisterRPC
publish(key)
root := findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
FindRootRPC
FindNextHop?
FindNextHop?�
Nope.�We are the root!
Register key to LocationMap
FindRootRPC
Root
Root
root.RegisterRPC(key, local.node)
RegisterRPC
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
4. RegisterRPC
publish(key)
root := findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
FindRootRPC
FindNextHop?
FindNextHop?�
Nope.�We are the root!
Register key to LocationMap
FindRootRPC
Root
Root
root.RegisterRPC(key, local.node)
RegisterRPC
Yup, we’re the root of this key
return true
Storing Object: Store(key string, value []byte)
Tapestry CLI: Store(key string, value []byte)
Local Node
Tap 1
Tap 2
Tap 3
4. Republish
publish(key)
root := findRootOnRemoteNode(local.node, key)
FindNextHop?
FindRootRPC
FindRootRPC
FindNextHop?
FindNextHop?�
Nope.�We are the root!
Register key to LocationMap
FindRootRPC
Root
Root
root.RegisterRPC(key, local.node)
RegisterRPC
Yup, we’re the root of this key
return true
Republish = publish → Cannot simply just call RegisterRPC for Republish. Must first find Root of Object on every Republish and then call RegisterRPC
Periodically republish
Joining Node: Join(otherNode RemoteNode)
Joining Node: Join(otherNode RemoteNode)
Local Node
Tap 1 (Joining Node)
Tap 2
Tap 3
Join(Tap2)
1. Find Root for joining Node
Joining Node: Join(otherNode RemoteNode)
Local Node
Not root
Root!
Tap 1 (Joining Node)
Tap 2
Tap 3
Join(Tap2)
1. Find Root for joining Node
tap2.FindRootRPC(tap1ID, 0)
tap3.FindRootRPC(tap1ID, 1)
Joining Node: Join(otherNode RemoteNode)
Local Node
Not root
Root!
Tap 1 (Joining Node)
Tap 2
Tap 3
Join(Tap2)
root := tap3
1. Find Root for joining Node
tap2.FindRootRPC(tap1ID, 0)
tap3.FindRootRPC(tap1ID, 1)
Root = tap3
Root = tap3
Joining Node: Join(otherNode RemoteNode)
Local Node
Not root
Root!
Tap 1 (Joining Node)
Tap 2
Tap 3
Join(Tap2)
root := tap3
2. AddNodeRPC to Root Node
tap2.FindRootRPC(tap1ID, 0)
tap3.FindRootRPC(tap1ID, 1)
Root = tap3
Root = tap3
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
root.AddNodeRPC(local.Node)
3. AddNodeMulticast
AddNodeMulticast(newNode, level)
targets = routingtable.get(level) // Must include local node
results = []
for target in targets
results.append(target.AddNodeMulticast(newNode, level + 1))
self.addRoute(newNode)
transferRelevantObjects(newNode)
return merge(results, targets)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
| 0 | 1 | ... |
xx... | ... | .. | .. |
1x… | .. | .. | .. |
11x.. | 110.. | | 112.. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
3. AddNodeMulticast
Get shared prefix with addNode
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
| 0 | 1 | ... |
xx... | ... | .. | .. |
1x… | .. | .. | .. |
11x.. | 110.. | | 112.. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
3. AddNodeMulticast
110..
112..
...
AddNodeMulticastRPC(addNode, P + 1) to all nodes at level P in local RT
Why P+1? Why not P?
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
| 0 | 1 | ... |
xx... | ... | .. | .. |
1x… | .. | .. | .. |
11x.. | 110.. | | 112.. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
3. AddNodeMulticast
110..
112..
...
AddNodeMulticastRPC(addNode, P + 1) to all nodes at level P in local RT
Repeat AddNodeMulticastRPC for next level down
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
| 0 | 1 | ... |
xx... | ... | .. | .. |
1x… | .. | .. | .. |
11x.. | 110.. | | 112.. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
3. AddNodeMulticast
110..
112..
...
AddNodeMulticastRPC(addNode, P + 1) to all nodes at level P in local RT
Repeat AddNodeMulticastRPC for next level down
Continue AddNodeMulticastRPC until last level
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
| 0 | 1 | ... |
xx... | ... | .. | .. |
1x… | .. | .. | .. |
11x.. | 110.. | | 112.. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
3. AddNodeMulticast
Reached end of routing table
In the last step:
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Root!
Tap 3
| 0 | 1 | ... |
xx... | ... | .. | .. |
1x… | .. | .. | .. |
11x.. | 110.. | | 112.. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
3. AddNodeMulticast
Wait for all downstream AddNodeMulticastRPC to finish
...
Return neighbors
110..
112..
root.AddNodeRPC(local.Node)
Joining Node: Join(otherNode RemoteNode)
Populate local Routing Table at level P with initial neighbor set
Local Node
Tap 1 (Joining Node)
neighbors := root.AddNodeRPC(local.Node)
4. Construct New Routing Table
| 0 | 1 | ... |
xx... | | | |
1x… | | | |
P | .. | .. | .. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
Neighbors
Joining Node: Join(otherNode RemoteNode)
Local Node
neighbors := root.AddNodeRPC(local.Node)
5. Populate rest of Routing Table (Levels: P → 0) (Backpointer Traversal)
| 0 | 1 | ... |
xx... | | | |
1x… | | | |
P | .. | .. | .. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
Tap 1 (Joining Node)
Joining Node: Join(otherNode RemoteNode)
Local Node
neighbors := root.AddNodeRPC(local.Node)
5. Populate rest of Routing Table (Levels: P → 0) (Backpointer Traversal)
| 0 | 1 | ... |
xx... | | | |
1x… | | | |
P | .. | .. | .. |
| | | |
| | | |
P := SharedPrefix(self, addNode)
Tap 1 (Joining Node)
Joining Node: Join(otherNode RemoteNode)
Local Node
neighbors := root.AddNodeRPC(local.Node)
Tap 1 (Joining Node)
6. Populate Backpointers
During AddNodeMulticast, whenever a remote node added local node to its Routing Table, it called joiner.AddBackpointerRPC(self)
This is implemented in addRoute()
Debugging
gRPC
Tapestry Paper
Closing Remarks
Expected lines of code: 500 ~ 700
→ Start early!
→ Read the handout/paper!
→ Come to TA hours.
→ Project Due: Sunday March 11th, 11:59 PM
FAQs from Years Past
How does FindNextHop(id, level) work?
In a particular slot in a row, the nodes in the slot should already be sorted by Closer()
FAQs from Years Past
How does id.IsNewRoute(newId, currentId) work?
From code comment: Given IDs newId and currentId, will id now route to newId?
FAQs from Years Past
From the paper: N uses the neighbor set to fill routing level p, trims the list to the closest k nodes and requests these nodes send their backpointers. What measure of closeness should we use for the k nodes during this backpointer traversal stage?
> Use Closer()!
FAQs from Years Past
Remove/AddBackpointer RPC clarifications:
FAQs from Years Past
FindRootonRemoteNode vs. FindRoot:
FAQs from Years Past
FindRoot has an argument `toRemove`. When should list of nodes in `toRemove` be removed in FindRoot?
tl;dr toRemove sets returned by all subcalls should be merged, removed and returned