MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / plan_graph_split

Function plan_graph_split

nodedb-cluster/src/shard_split.rs:112–201  ·  view source on GitHub ↗

Plan a graph-aware shard split. Uses BFS-based community detection to partition nodes into two groups that minimize cross-shard edges. Starting from a seed node, BFS assigns the first half of discovered nodes to group A, the rest to group B. This is a greedy heuristic — not optimal like METIS, but O(V+E) and practical for online splitting without blocking queries.

(
    source_vshard: u32,
    new_vshard: u32,
    target_node: u64,
    node_ids: &[String],
    edges: &[(String, String)],
)

Source from the content-addressed store, hash-verified

110/// This is a greedy heuristic — not optimal like METIS, but O(V+E) and
111/// practical for online splitting without blocking queries.
112pub fn plan_graph_split(
113 source_vshard: u32,
114 new_vshard: u32,
115 target_node: u64,
116 node_ids: &[String],
117 edges: &[(String, String)],
118) -> SplitPlan {
119 if node_ids.is_empty() {
120 return SplitPlan {
121 source_vshard,
122 new_vshard,
123 target_node,
124 documents_to_move: Vec::new(),
125 strategy: SplitStrategy::GraphCommunity,
126 estimated_bytes: 0,
127 };
128 }
129
130 // Build adjacency list for BFS.
131 let mut adj: HashMap<&str, Vec<&str>> = HashMap::new();
132 for (src, dst) in edges {
133 adj.entry(src.as_str()).or_default().push(dst.as_str());
134 adj.entry(dst.as_str()).or_default().push(src.as_str());
135 }
136
137 // BFS from first node, assign first half to group A.
138 let mut visited: Vec<String> = Vec::with_capacity(node_ids.len());
139 let mut seen: HashSet<&str> = HashSet::new();
140 let mut queue: std::collections::VecDeque<&str> = std::collections::VecDeque::new();
141
142 // Start BFS from the node with the most edges (hub).
143 let start = node_ids
144 .iter()
145 .max_by_key(|id| adj.get(id.as_str()).map(|v| v.len()).unwrap_or(0))
146 .map(|s| s.as_str())
147 .unwrap_or(node_ids[0].as_str());
148
149 queue.push_back(start);
150 seen.insert(start);
151
152 while let Some(node) = queue.pop_front() {
153 visited.push(node.to_string());
154 if let Some(neighbors) = adj.get(node) {
155 for &neighbor in neighbors {
156 if seen.insert(neighbor) {
157 queue.push_back(neighbor);
158 }
159 }
160 }
161 }
162
163 // Add any disconnected nodes not reached by BFS.
164 for id in node_ids {
165 if seen.insert(id.as_str()) {
166 visited.push(id.clone());
167 }
168 }
169

Callers 2

graph_split_emptyFunction · 0.85

Calls 15

entryMethod · 0.80
pop_frontMethod · 0.80
to_stringMethod · 0.80
collectMethod · 0.80
is_emptyMethod · 0.45
pushMethod · 0.45
as_strMethod · 0.45
lenMethod · 0.45
iterMethod · 0.45
getMethod · 0.45
insertMethod · 0.45
cloneMethod · 0.45

Tested by 2

graph_split_emptyFunction · 0.68