GroupRouting
Move Type: Group Complexity: O(groups × routing_rings) - routing-aware Primary Use: Latency-optimized group placement with routing awareness
Move groups of objects based on routing configuration and latency awareness. Places objects to minimize routing latency from sources to destinations.
Overview
GroupRouting (also known as GROUP_ROUTING) is a specialized move type that evaluates move sets for every group based on a routing configuration. For each group and each source in its routing rings, it attempts to place objects in minimum-latency destinations from the source.
Use when:
- Have routing configuration defined
- Optimizing for network latency
- Objects organized in groups with routing requirements
- Need routing-aware placement
- Have origin-to-destination latency tables
Avoid when:
- No routing configuration available
- Latency not a concern
- Simple placement without routing awareness
- No group structure
Quick Example
- Python
- C++
def quick_example():
"""Quick example showing basic GroupRouting usage."""
solver = ProblemSolver(service_name="example", service_scope="test")
solver.setObjectName("shard")
solver.setContainerName("server")
# Define partitions
solver.addPartition(
"table", {"embeddings": ["shard1", "shard2"], "features": ["shard3", "shard4"]}
)
# Route groups based on latency configuration
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
GroupRoutingMoveTypeSpec(
routingConfigName="latency-aware",
),
]
)
)
)
# Setup problem
solver.setAssignment(
{
"server1": ["shard1", "shard3"],
"server2": ["shard2"],
"server3": ["shard4"],
}
)
solver.addObjectDimension(
"size",
{
"shard1": 1.0,
"shard2": 1.0,
"shard3": 1.0,
"shard4": 1.0,
},
)
solver.addGoal(
GoalSpecs(
balanceSpec=BalanceSpec(
name="balance-size", scope="server", dimension="size"
)
),
weight=1.0,
)
# Solve and print results
solution = solver.solve()
print(f" Initial objective: {solution.initialObjective:.4f}")
print(f" Final objective: {solution.finalObjective:.4f}")
print(f" Improvement: {solution.initialObjective - solution.finalObjective:.4f}")
void quickExample() {
// Route groups based on latency configuration
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("shard");
solver.setContainerName("server");
// Setup problem
solver.setAssignment(
std::map<std::string, std::vector<std::string>>{
{"server1", {"shard1", "shard3"}},
{"server2", {"shard2"}},
{"server3", {"shard4"}},
});
solver.addObjectDimension(
"size",
std::map<std::string, double>{
{"shard1", 1.0},
{"shard2", 1.0},
{"shard3", 1.0},
{"shard4", 1.0},
});
BalanceSpec balanceSpec;
balanceSpec.name() = "balance-size";
balanceSpec.scope() = "server";
balanceSpec.dimension() = "size";
solver.addGoal(balanceSpec, 1.0);
// Define partitions
solver.addPartition(
"table",
std::unordered_map<std::string, std::vector<std::string>>{
{"embeddings", {"shard1", "shard2"}},
{"features", {"shard3", "shard4"}},
});
LocalSearchSolverSpec solverSpec;
GroupRoutingMoveTypeSpec routingSpec;
routingSpec.routingConfigName() = "latency-aware";
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().groupRoutingMoveTypeSpec() = routingSpec;
solver.addSolver(solverSpec);
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
routingConfigName | string | Yes | null | Name of routing configuration |
unassignedContainer | string | No | null | Container for unassigned objects |
Parameter Details
routingConfigName:
- Name of the routing configuration to use
- References a
RoutingLatencySpecdefined in the problem - Specifies routing rings, latency tables, and routing policy
- Required for this move type to function
unassignedContainer:
- Optional container name for initially unassigned objects
- Enables moving objects from unassigned state
- If not specified, only moves already-assigned objects
How It Works
For each group in the partition:
- Identify routing rings: Determine routing rings for this group from config
- For each source: For each source in the routing rings:
- Find min-latency destination: Identify destination with minimum latency from this source
- Generate move: Create move to place object at min-latency destination
- Evaluate move set: Test the complete move set for this group
- Repeat for all groups: Process all groups in partition
- Select best: Choose the move set that improves objective most
Visual Example
Routing Configuration: latency-aware placement
Group1 (routing rings: source1, source2):
source1 → destination_A (latency: 5ms) ✓ Best
source1 → destination_B (latency: 20ms)
source2 → destination_C (latency: 3ms) ✓ Best
source2 → destination_D (latency: 15ms)
Group2 (routing rings: source3, source4):
source3 → destination_A (latency: 10ms) ✓ Best
source3 → destination_C (latency: 25ms)
source4 → destination_B (latency: 8ms) ✓ Best
source4 → destination_D (latency: 30ms)
For each group, place objects in min-latency destinations
Complexity
Per iteration: O(G × R)
Where:
- G = number of groups in partition
- R = average routing rings per group
Evaluation: One move set per group, evaluated in parallel
Usage Patterns
Basic Routing
Routing-aware placement with latency optimization:
- Python
- C++
def basic_routing():
"""Place groups based on routing configuration to minimize latency."""
solver = ProblemSolver(service_name="example", service_scope="test")
solver.setObjectName("shard")
solver.setContainerName("server")
solver.addPartition("table", {"embeddings": [], "features": []})
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
GroupRoutingMoveTypeSpec(
routingConfigName="cdn-latency",
),
]
)
)
)
void basicRouting() {
// Place groups based on routing configuration to minimize latency
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("shard");
solver.setContainerName("server");
std::unordered_map<std::string, std::vector<std::string>> tables = {
{"embeddings", {}},
{"features", {}},
};
solver.addPartition("table", std::move(tables));
LocalSearchSolverSpec solverSpec;
GroupRoutingMoveTypeSpec routingSpec;
routingSpec.routingConfigName() = "cdn-latency";
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().groupRoutingMoveTypeSpec() = routingSpec;
solver.addSolver(solverSpec);
}
With Unassigned Container
Enable placement from unassigned state:
- Python
- C++
def unassigned():
"""Use unassigned container to enable placement from unassigned state."""
solver = ProblemSolver(service_name="example", service_scope="test")
solver.addPartition("service", {"web": [], "api": []})
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
GroupRoutingMoveTypeSpec(
routingConfigName="edge-latency",
unassignedContainer="unassigned",
),
]
)
)
)
void unassigned() {
// Use unassigned container to enable placement from unassigned state
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
std::unordered_map<std::string, std::vector<std::string>> services = {
{"web", {}},
{"api", {}},
};
solver.addPartition("service", services);
LocalSearchSolverSpec solverSpec;
GroupRoutingMoveTypeSpec routingSpec;
routingSpec.routingConfigName() = "edge-latency";
routingSpec.unassignedContainer() = "unassigned";
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().groupRoutingMoveTypeSpec() = routingSpec;
solver.addSolver(solverSpec);
}
Multi-Region Routing
Optimize placement across regions:
- Python
- C++
# Optimize cross-region placement based on origin-to-destination latency
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
GroupRoutingMoveTypeSpec(
routingConfigName="cross-region-latency",
),
]
)
)
)
void multiRegion() {
// Optimize cross-region placement based on origin-to-destination latency
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("replica");
solver.setContainerName("datacenter");
std::unordered_map<std::string, std::vector<std::string>> services = {
{"storage", {}},
{"compute", {}},
};
solver.addPartition("service", std::move(services));
LocalSearchSolverSpec solverSpec;
GroupRoutingMoveTypeSpec routingSpec;
routingSpec.routingConfigName() = "cross-region-latency";
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().groupRoutingMoveTypeSpec() = routingSpec;
solver.addSolver(solverSpec);
// Setup multi-region problem
solver.setAssignment(
std::map<std::string, std::vector<std::string>>{
{"datacenter1", {"replica1", "replica3"}},
{"datacenter2", {"replica2"}},
{"datacenter3", {"replica4"}},
});
solver.addPartition(
"service",
std::unordered_map<std::string, std::vector<std::string>>{
{"storage", {"replica1", "replica2"}},
{"compute", {"replica3", "replica4"}},
});
solver.addObjectDimension(
"load",
std::map<std::string, double>{
{"replica1", 2.0},
{"replica2", 2.0},
{"replica3", 3.0},
{"replica4", 3.0},
});
BalanceSpec balanceSpec;
balanceSpec.name() = "balance-load";
balanceSpec.scope() = "datacenter";
balanceSpec.dimension() = "load";
solver.addGoal(std::move(balanceSpec), 1.0);
// Solve and print results
auto solution = solver.solve();
std::cout << " Initial objective: " << std::fixed << std::setprecision(4)
<< *solution.initialObjective()->value() << "\n";
std::cout << " Final objective: " << *solution.finalObjective()->value()
<< "\n";
std::cout << " Improvement: "
<< (*solution.initialObjective()->value() -
*solution.finalObjective()->value())
<< "\n";
}
Performance Characteristics
When Does It Help?
GroupRouting helps when:
- Latency-sensitive: Network latency impacts performance
- Routing awareness: Have routing configuration and latency tables
- Group-based routing: Objects in groups with specific routing needs
- Geographic distribution: Optimizing across regions/datacenters
- CDN/edge placement: Content delivery optimization
GroupRouting does NOT help when:
- No routing config: Don't have routing configuration defined
- Latency insensitive: Routing latency not important
- No group structure: Objects not organized in groups
- Simple placement: Basic placement without routing awareness
Comparison with Alternatives
| Move Type | Routing Aware | Latency Optimized | Use Case |
|---|---|---|---|
| Single | No | No | General placement |
| ColocateGroups | No | No | Group colocation |
| GroupRouting | Yes | Yes | Latency-optimized routing |
Troubleshooting
Problem: No improving moves found
Diagnosis: Routing configuration may not allow beneficial moves
Solutions:
- Verify routing configuration is correct
- Check latency tables are populated
- Ensure groups have routing rings defined
- May already be optimally routed
- Review capacity constraints on destinations
Problem: Routing config not found
Diagnosis: routingConfigName doesn't match defined configs
Solutions:
- Verify routing configuration name is correct
- Check
RoutingLatencySpecis defined in problem - Review routing config setup in problem definition
Problem: Objects not moving to expected destinations
Diagnosis: Latency tables or routing rings issue
Solutions:
- Verify latency tables have correct values
- Check routing rings are defined for all groups
- Review partition and group assignments
- Ensure destinations have capacity
Problem: Unassigned objects not moving
Diagnosis: Missing unassignedContainer parameter
Solutions:
- Set
unassignedContainerparameter - Verify unassigned container name is correct
- Check that objects are in unassigned container
When to Use GroupRouting
DO use when:
- Have routing configuration defined
- Optimizing for network latency
- Objects in groups with routing requirements
- Need routing-aware placement
- Have latency tables available
DO NOT use when:
- No routing configuration
- Latency not important
- No group structure
- Simple placement sufficient
Related Move Types
Group-based alternatives:
- ColocateGroups - Collocate related groups
- GroupMoveWithHintStrategies - Strategy hints for groups
- GreedyGroupToScopeItem - Greedy group placement
General alternatives:
- Single - General single moves
- SingleGreedy - Greedy single moves
Source Code
- Thrift definition:
interface/thrift/SolverSpecs.thrift:563 - Implementation:
solver/moves/GroupRoutingMoveType.h - Tests:
solver/moves/tests/
Next Steps
- Try ColocateGroups for group colocation
- Review Move Types Overview for choosing move types
- See routing documentation for latency table setup
Notes
⚠️ Advanced Move Type: GroupRouting is a specialized move type requiring routing configuration setup. Refer to routing documentation and examples for proper configuration.