The supernode breaks the promise

A supernode's relationship chain is enormous, so expanding through it scans millions of edges and the O(1)-per-hop promise collapses on that single node — which is why you traverse from the low-degree side (The Rock has 232M inbound but few outbound) and group its edges by type and direction.

Previously

The planner anchors on the few, not the many — but what if the 'many' is unavoidable because a single node has millions of edges? That's a supernode, and expanding through its giant relationship chain scans millions of records: the O(1)-per-hop promise collapses on that one node. Reads are only half the damage, though — what happens when everyone tries to ADD an edge to that same celebrity at once?

Scene 09

The supernode breaks the promise

  1. Watch
  2. Try it
  3. Predict
  4. Capture
SUPERNODE232M inbound ×10Mtraversal drowns — never finishesAliceBobCarolDaveErin5FrankGraceHeidiIvanJudyThe Rock×10MThe MatrixInceptionGothamFRIENDRATEDLIVES_INFOLLOWSErin has 5 edges — a normal node. The Rock has ~20 drawn here, each standing for 10M. A walk IN…SPINElocal 4 · global 20Local stops being small — the…
What to watch for

Two nodes, side by side. Erin has five relationship records — a normal node; a traversal that grazes her lights just a few neighbors. The Rock has so many followers that we can only draw a fan, each edge labeled ×10M (it really has ~232M). Watch a traversal try to expand INTO The Rock: it has to walk its entire relationship chain. The scan floods the fan… and stalls. It can't finish — every hop the curriculum promised was O(1) just turned into 'scan millions of edges' on this one node.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Traversal.expand
a hop walks the node's relationship chain — O(degree), not O(1)
def expand(node, rel_type, direction):
out = []
# walk the doubly-linked relationship chain for this side
rel = node.first_rel(rel_type, direction)
while rel is not None: # O(degree) — the whole cost
out.append(rel.other_end(node))
rel = rel.next_in_chain # one pointer follow per edge
return out
# inbound on The Rock: ~232M iterations → drowns
# outbound on The Rock: ~3 iterations → instant
Planner.pickExpandSide
expand cost is O(degree) on that side, so walk whichever direction is shorter
def pick_expand_side(node, rel_type):
# cost of a hop = how many edges hang off this side
in_deg = node.degree(rel_type, INBOUND)
out_deg = node.degree(rel_type, OUTBOUND)
# walk from the low-degree side — fewer pointer follows
if in_deg <= out_deg:
return INBOUND
return OUTBOUND
# The Rock: in_deg ~232M, out_deg ~3 → pick OUTBOUND
# (same node, opposite cost — degree, not the node, is the bill)
Store.denseNodeGroups
a dense node splits its one mixed chain into per-(type, direction) groups
DENSE_THRESHOLD = 50 # ≥ this many edges → 'dense' layout
def first_rel(node, rel_type, direction):
if node.degree() < DENSE_THRESHOLD:
return node.head_of_single_chain # one mixed chain
# dense: jump straight to the right group's head
grp = node.group_record(rel_type, direction)
return grp.chain_head # no scan to locate it
# so EXPAND lands on the short chain directly,
# never touching the giant one beside it

Where this sits in Build a graph database (Neo4j / Dgraph-style)

Scene 09 of 16, in the It breaks act — The supernode shatters the O(1)-per-hop promise.. A supernode is a vertex with a huge degree, and its relationship chain is so long that any traversal THROUGH it must scan millions of edges — so the O(1)-per-hop promise dies on that one node, and the fix is to expand from the low-degree side.

Up next. A supernode wrecks reads by being a long chain to scan. Now flip to writes: historically, adding an edge took a lock on the WHOLE node, so every concurrent 'follow The Rock' serialized on one lock and throughput collapsed. Fixing that needed a finer-grained lock — and it's a clean little half-step on the same supernode picture.

All 16 scenes in Build a graph database (Neo4j / Dgraph-style) · Every curriculum

Built with Arqly
Every scene in Build a graph database (Neo4j / Dgraph-style) builds on the one before it.All 16 Build a graph database (Neo4j / Dgraph-style) scenes