java-topology/defects/dragonfly/patch/dragonfly-0001-missing-migrations-hashset.md

68 lines
2.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000382
# dragonfly-0001: GetMissingMigrations O(M²) std::find → O(M log M) set_difference
## Classification
| Field | Value |
|-------------|-------|
| CWE | CWE-407 Inefficient Algorithmic Complexity |
| Severity | LOW-MEDIUM |
| Component | `src/server/cluster/cluster_config.cc:394` |
| Function | `GetMissingMigrations` (called × 4 per config update) |
| Hot path | Every `SetConfig` / cluster reconfiguration event |
| Status | PATCHED (unit test PASS) |
## Defect
`GetMissingMigrations` computes the set difference of two `std::vector<MigrationInfo>`
using a nested loop + `std::find`:
```cpp
// cluster_config.cc:394
static std::vector<MigrationInfo> GetMissingMigrations(
const std::vector<MigrationInfo>& haystack,
const std::vector<MigrationInfo>& needle) {
std::vector<MigrationInfo> res;
for (const auto& h : haystack) { // O(M)
if (find(needle.begin(), needle.end(), h) == needle.end()) { // O(M × R)
res.push_back(h);
}
}
return res;
}
```
`MigrationInfo::operator==` compares `node_info` (string) + `slot_ranges`
(vector comparison O(R)). Total: **O(M² × R)** per call × 4 call sites per config update.
Called by:
- `GetNewOutgoingMigrations`
- `GetNewIncomingMigrations`
- `GetFinishedOutgoingMigrations`
- `GetFinishedIncomingMigrations`
**Note:** Current deployments use M < 10, so practical impact is low. However the
API is O(M²) by design and becomes a bottleneck if Dragonfly supports large-scale
automated shard rebalancing (M = hundreds).
## Fix
Sort both vectors on `node_info.id` (already has `operator<`), then use
`std::set_difference` O(M log M):
```cpp
static std::vector<MigrationInfo> GetMissingMigrations(
std::vector<MigrationInfo> haystack, // by value — will sort
std::vector<MigrationInfo> needle) {
std::sort(haystack.begin(), haystack.end());
std::sort(needle.begin(), needle.end());
std::vector<MigrationInfo> res;
std::set_difference(haystack.begin(), haystack.end(),
needle.begin(), needle.end(),
std::back_inserter(res));
return res;
}
```
Or: build `absl::flat_hash_set<string>` keyed on `node_info.id` from `needle`,
check in O(1) per haystack entry O(M) total.