Edge-cut, vertex-cut, predicate sharding
On power-law graphs edge-cut (one vertex per machine) dumps a hub's edges onto one machine as a hotspot, so vertex-cut splits the high-degree vertex across machines to balance edges; predicate-sharding (Dgraph) sidesteps the choice by keeping every edge of one TYPE together so 'all of X's friends' stays local — but no cut has zero crossing edges.
Cutting is unavoidable; the question was which cut crosses fewest edges. Edge-cut hotspots on hubs; vertex-cut splits the hub to balance edges; predicate-sharding keeps each edge type together so common expands stay local. The supernode broke naive partitioning, just as it broke reads and writes. But all of this is about TRAVERSAL by edges — what about finding a start node by its VALUE across a distributed store?
Scene 11a
Edge-cut, vertex-cut, predicate sharding
- Watch
- Try it
- Predict
- Capture
Same social graph, but it no longer fits on one machine — we have to cut it across two. Watch the simplest cut first: give every PERSON to one machine and cut whatever edges span the two. Step 1, the cut line drops. Step 2, look what happens to The Rock — it's a hub, so when its vertex lands on machine 1, ALL of its edges land there too. One machine ends up carrying half the graph's edges: a hotspot.
Highlighted lines are the ones running in the diagram right now.
def place(graph, mode, M): # M machinesif mode == 'edge-cut':for v in graph.vertices: # vertex → one machinehome[v] = hash(v) % M # a hub's edges all follow velif mode == 'vertex-cut':for e in graph.edges: # edge → one machinehome[e] = hash(e) % M # split hubs: mirror across Melif mode == 'predicate':for e in graph.edges: # edge TYPE → one shardhome[e] = shard_of[e.type] # all of one type togetherreturn home
def expand_friends(X, mode):if mode == 'predicate':return read(shard_of['FRIEND']) # ONE machine, local# otherwise X's :FRIEND edges may live on several machinesmachines = {home[e] for e in edges(X, 'FRIEND')}return rpc_fanout(machines) # cross-cut network hops
def assign(graph, M): # one pass, heuristicfor v in stream(graph.vertices):# score each machine: keep neighbors together, stay balancedbest = argmax(m in machines,neighbors_on(v, m) - lambda * load[m])home[v] = best # greedy, not optimalload[best] += 1# whatever's left spanning machines is the unavoidable cut:crossings = count(e for e in edgesif home[e.from] != home[e.to])return home, crossings # crossings > 0 always
Where this sits in Build a graph database (Neo4j / Dgraph-style)
Scene 11a of 16, in the Scale & ACID act — ACID on one box; the partition cut turns hops to RPCs.. Edge-cut assigns each vertex to a machine and chokes on power-law hubs; vertex-cut splits the hub across machines to balance edges; predicate-sharding keeps all edges of one TYPE together so a common expand stays on one machine.
Up next. Sharding decided where edges live, but every query still has to find its ANCHOR — by name, by text, by range. That SEEK was never part of adjacency (scene 5), and it isn't part of sharding either. In distributed graph stores, finding start nodes by value is served by classic indexes — full-text, B-tree, vector — bolted on the side, not woven into the native pointer-chase.
All 16 scenes in Build a graph database (Neo4j / Dgraph-style) · Every curriculum