FixedSourceMultiMove
Move Type: Fixed Complexity: O(bundles × bundle_size) with parallel evaluation Primary Use: RAS stackable solve
Move bundles of related objects from a specific fixed source to hot container. The inverse of FixedDestMultiMove, designed for RAS local search.
Overview
FixedSourceMultiMove (also known as FIXED_SOURCE_MULTIPLE) evaluates moving bundles of related objects from a single predetermined source container to the hot (underutilized) container. Unlike FixedSource which moves one object at a time, this move type moves entire object bundles together.
Use when:
- Using RAS stackable solve
- Draining bundles from specific source
- Know exactly which source to pull from
- Objects must move as coordinated groups
- Hot container needs filling with bundles
- Want parallel evaluation of move sets
Avoid when:
- Not using RAS local search
- Objects can move independently (use FixedSource)
- Don't know source (use Single)
- Need to explore multiple sources
- Want solver to find best sources
Quick Example
- Python
- C++
# Move object bundles from specific source to hot container
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedSrcMultiMoveTypeSpec(
specialContainer="old_server", # Fixed source
),
]
)
)
)
void quickExample() {
// Move object bundles from specific source to hot container
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("task");
solver.setContainerName("server");
LocalSearchSolverSpec solverSpec;
FixedSrcMultiMoveTypeSpec fixedSrcMultiSpec;
fixedSrcMultiSpec.specialContainer() = "old_server"; // Fixed source
// RasLocalSearchMetadata is required for multi-move types
auto rasMetadata = std::make_shared<const RasLocalSearchMetadata>();
fixedSrcMultiSpec.rasLocalSearchMetadata() = std::move(rasMetadata);
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedSrcMultiMoveTypeSpec() =
fixedSrcMultiSpec;
solver.addSolver(solverSpec);
// Setup drain scenario
solver.setAssignment(
std::map<std::string, std::vector<std::string>>{
{"old_server", {"task0", "task1", "task2"}},
{"server1", {"task3"}},
{"server2", {}},
});
solver.addObjectDimension(
"cpu",
std::map<std::string, double>{
{"task0", 1.0},
{"task1", 1.0},
{"task2", 1.0},
{"task3", 3.0},
});
BalanceSpec balanceSpec;
balanceSpec.name() = "balance-cpu";
balanceSpec.scope() = "server";
balanceSpec.dimension() = "cpu";
solver.addGoal(balanceSpec, 1.0);
// Note: solve() is not called here because multi-move types require
// additional bundle/partition setup (e.g., search space partitioning)
// that is beyond the scope of this basic example.
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
specialContainer | string | Yes | null | Fixed source container name |
maxSamplesPerEquivSet | int | No | 5 | Max move sets to consider per equivalent set |
rasLocalSearchMetadata | RasLocalSearchMetadata | No | null | Metadata for RAS local search |
Parameter Details
specialContainer:
- Name of the specific source container
- All object bundles will be pulled from this container only
- Must be a valid container name
maxSamplesPerEquivSet:
- Limits move sets evaluated per equivalent set
- Equivalent sets are groups identical from local search perspective
- Higher values = better quality, more computation
- Default: 5 samples per equivalent set
rasLocalSearchMetadata:
- Metadata specific to RAS stackable solve
- Optional configuration for RAS local search behavior
- Only relevant for RAS use cases
How It Works
Given a hot container (underutilized destination):
- Select bundle: Pick object bundle from
specialContainer(source) - Evaluate move: Test moving all objects in bundle to hot container
- Parallel evaluation: All move sets evaluated in parallel (multi-threading)
- Sample: Consider up to
maxSamplesPerEquivSetper equivalent set - Repeat: Try all bundles in special source container
- Apply best: Apply the bundle move that improves objective most
Visual Example
Before move: After bundle move from specialContainer:
┌──────────────┐ ┌──────────────┐
│ Hot │ │ Hot │
│ Container │ <────────── │ Container │
│ (empty) │ ┌──┤ Bundle1 ←───┼── Pulled from source!
└──────────────┘ │ │ • obj1 │
│ │ • obj2 │
┌──────────────┐ │ └──────────────┘
│ Special │ │
│ Container │ ────────────────┘ ┌──────────────┐
│ Bundle1 ────┼──┐ │ Special │
│ • obj1 │ │ │ Container │
│ • obj2 ────┼──┘ Source │ Bundle2 │
│ Bundle2 │ │ • obj3 │
│ • obj3 │ │ • obj4 │
│ • obj4 │ └──────────────┘
└──────────────┘
Entire bundle moves from specialContainer to hot container
Comparison with FixedDestMultiMove
| Aspect | FixedDestMultiMove | FixedSourceMultiMove |
|---|---|---|
| Fixed | Destination | Source |
| Hot container | Source (gives) | Destination (receives) |
| Use case | Push bundles to dest | Pull bundles from source |
| Move direction | Hot → Fixed | Fixed → Hot |
Complexity
Per iteration: O(B × S)
Where:
- B = number of bundles in special source container
- S = average bundle size (objects per bundle)
Sampling: Limited by maxSamplesPerEquivSet per equivalent set
Example - RAS draining:
- Source container bundles: 20
- Average bundle size: 4 objects
- maxSamplesPerEquivSet: 5
- Evaluations: ~20 bundles (limited by sampling), 4 objects each
- Benefit: Parallel evaluation across bundles
Usage Patterns
Drain RAS Server
Pull RAS bundles from server being decommissioned:
- Python
- C++
# Drain RAS task bundles from server being decommissioned
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedSrcMultiMoveTypeSpec(
specialContainer="old_ras_server", # Server to drain
),
]
)
)
)
void drainServer() {
// Drain RAS task bundles from server being decommissioned
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("task");
solver.setContainerName("server");
LocalSearchSolverSpec solverSpec;
FixedSrcMultiMoveTypeSpec fixedSrcMultiSpec;
fixedSrcMultiSpec.specialContainer() = "old_ras_server"; // Server to drain
auto rasMetadata = std::make_shared<const RasLocalSearchMetadata>();
fixedSrcMultiSpec.rasLocalSearchMetadata() = std::move(rasMetadata);
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedSrcMultiMoveTypeSpec() =
fixedSrcMultiSpec;
solver.addSolver(solverSpec);
// Setup RAS drain problem
solver.setAssignment(
std::map<std::string, std::vector<std::string>>{
{"old_ras_server", {"task0", "task1", "task2", "task3"}},
{"server1", {"task4"}},
{"server2", {"task5"}},
{"server3", {}},
});
solver.addObjectDimension(
"memory",
std::map<std::string, double>{
{"task0", 1.5},
{"task1", 1.5},
{"task2", 2.0},
{"task3", 2.0},
{"task4", 3.0},
{"task5", 3.0},
});
BalanceSpec balanceSpec;
balanceSpec.name() = "balance-memory";
balanceSpec.scope() = "server";
balanceSpec.dimension() = "memory";
solver.addGoal(std::move(balanceSpec), 1.0);
// Note: solve() is not called here because multi-move types require
// additional bundle/partition setup (e.g., search space partitioning)
// that is beyond the scope of this basic example.
}
Fill Underutilized Container
Pull bundles from specific source to fill hot container:
- Python
- C++
# Fill underutilized container by pulling bundles from specific source
solver = ProblemSolver(service_name="example", service_scope="test")
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedSrcMultiMoveTypeSpec(
specialContainer="overloaded_server",
),
]
)
)
)
void fillContainer() {
// Fill underutilized container by pulling bundles from specific source
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
LocalSearchSolverSpec solverSpec;
FixedSrcMultiMoveTypeSpec fixedSrcMultiSpec;
fixedSrcMultiSpec.specialContainer() = "overloaded_server";
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedSrcMultiMoveTypeSpec() =
fixedSrcMultiSpec;
solver.addSolver(solverSpec);
}
Sampling Control
Control sampling of equivalent sets:
- Python
- C++
# Limit evaluations by sampling equivalent sets
solver = ProblemSolver(service_name="example", service_scope="test")
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedSrcMultiMoveTypeSpec(
specialContainer="source_server",
maxSamplesPerEquivSet=10, # Evaluate up to 10 per equiv set
),
]
)
)
)
void sampling() {
// Limit evaluations by sampling equivalent sets
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
LocalSearchSolverSpec solverSpec;
FixedSrcMultiMoveTypeSpec fixedSrcMultiSpec;
fixedSrcMultiSpec.specialContainer() = "source_server";
fixedSrcMultiSpec.maxSamplesPerEquivSet() =
10; // Evaluate up to 10 per equiv set
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedSrcMultiMoveTypeSpec() =
fixedSrcMultiSpec;
solver.addSolver(solverSpec);
}
With RAS Metadata
Configure with RAS local search metadata:
- Python
- C++
# RAS stackable solve with metadata
solver = ProblemSolver(service_name="example", service_scope="test")
from rebalancer.interface.thrift.v2.SolverSpecs.thrift_types import (
RasLocalSearchMetadata,
)
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedSrcMultiMoveTypeSpec(
specialContainer="ras_source",
maxSamplesPerEquivSet=5,
rasLocalSearchMetadata=RasLocalSearchMetadata(),
),
]
)
)
)
void rasMetadata() {
// RAS stackable solve with metadata
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
LocalSearchSolverSpec solverSpec;
auto rasMetadata = std::make_shared<const RasLocalSearchMetadata>();
// Configure RAS metadata as needed
FixedSrcMultiMoveTypeSpec fixedSrcMultiSpec;
fixedSrcMultiSpec.specialContainer() = "ras_source";
fixedSrcMultiSpec.maxSamplesPerEquivSet() = 5;
fixedSrcMultiSpec.rasLocalSearchMetadata() = std::move(rasMetadata);
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedSrcMultiMoveTypeSpec() =
fixedSrcMultiSpec;
solver.addSolver(solverSpec);
}
Performance Characteristics
Speedup Analysis
| Bundles | Bundle Size | FixedSourceMultiMove | Single | Speedup |
|---|---|---|---|---|
| 20 | 4 | 80 | 100K | ~1250× |
| 50 | 8 | 400 | 1M | ~2500× |
| 100 | 10 | 1K | 10M | ~10000× |
Benefits:
- Parallel evaluation: All move sets evaluated concurrently
- Bundle efficiency: Move related objects together
- Sampling control:
maxSamplesPerEquivSetlimits computation - Fixed source: No source exploration overhead
When Does It Help?
FixedSourceMultiMove helps when:
- RAS stackable solve: Designed for RAS local search
- Bundle coordination: Objects must move together
- Known source: Exactly which source to drain
- Filling hot container: Underutilized container needs bundles
- Parallel benefits: Can leverage multi-threading
FixedSourceMultiMove does NOT help when:
- Not RAS: Use FixedSource for single objects
- Independent objects: Objects can move separately
- Unknown source: Need solver to find best source
- Exploring options: Want to try multiple sources
Comparison with Variants
| Move Type | Destination | Source | Move Unit | Use Case |
|---|---|---|---|---|
| FixedSource | Hot container | Fixed specific | Single object | General pull from source |
| FixedSourceMultiMove | Hot container | Fixed specific | Object bundle | RAS pull from source |
| FixedDestMultiMove | Fixed specific | Hot container | Object bundle | RAS push to dest |
| FixedDestSwapMultiMove | Fixed specific | Hot container | Bundle swap | RAS swaps |
Decision tree:
- RAS stackable + know source? → FixedSourceMultiMove
- RAS stackable + know dest? → FixedDestMultiMove
- Single objects + know source? → FixedSource
- Neither? → Single
Troubleshooting
Problem: No improving moves found
Diagnosis: Bundles can't beneficially move from source to hot container
Solutions:
- Verify
specialContaineris correct source - Check capacity constraints on hot container
- Bundle sizes may be too large for destination
- May already be optimal
- Try different source or Single
Problem: Wrong bundles moving
Diagnosis: Bundle formation or objective function issue
Solutions:
- Verify bundle formation logic is correct
- Check objective function rewards correct bundle moves
- Review equivalent set definitions
- May need different objective or constraints
- Examine which bundles the solver is selecting
Problem: Too many evaluations
Diagnosis: Too many bundles or large equivalent sets
Solutions:
- Reduce
maxSamplesPerEquivSet(default: 5) - Start with smaller sample (e.g., 2-3 per set)
- Review bundle formation to reduce bundle count
- Check if bundles can be simplified
Problem: Hot container not filling
Diagnosis: Capacity constraints or bundles don't fit
Solutions:
- Verify hot container has capacity for bundles
- Check objective function rewards filling hot container
- Bundle sizes may exceed hot container capacity
- May need different source
- Review capacity constraints
When to Use FixedSourceMultiMove
DO use when:
- Using RAS stackable solve
- Draining bundles from specific source
- Know exactly which source to pull from
- Objects must move as coordinated groups
- Hot container needs filling with bundles
DO NOT use when:
- Not using RAS local search
- Objects can move independently (use FixedSource)
- Don't know source
- Need to explore multiple sources
- General optimization (use Single)
Related Move Types
Fixed bundle variants:
- FixedSource - Single object from fixed source
- FixedSourceMultiMove - Bundle from fixed source (this)
- FixedDestMultiMove - Bundle to fixed dest
- FixedDestSwapMultiMove - Bundle swaps to fixed dest
RAS move types:
- All three above are used exclusively for RAS stackable solve
- Work together in RAS local search strategies
General alternatives:
- FixedSource - Single objects from fixed source
- Single - Explore all sources
Source Code
- Thrift definition:
interface/thrift/SolverSpecs.thrift:588 - Implementation:
solver/moves/FixedSrcMultiMoveType.h - Tests:
solver/moves/tests/
Next Steps
- Learn about FixedDestMultiMove for pushing bundles to specific destination
- Try FixedDestSwapMultiMove for bundle swaps
- Review FixedSource for single-object moves
- See Move Types Overview for choosing move types