Skip to content

Commit e119ed8

Browse files
committed
fix: stop node detection after ten usable nodes
1 parent 9d9119e commit e119ed8

3 files changed

Lines changed: 129 additions & 80 deletions

File tree

‎src-tauri/src/core.rs‎

Lines changed: 114 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ const SYSTEM_NODE_CATALOG_KEY: [u8; 32] = [
3838
const MAX_SYSTEM_NODES: usize = 100;
3939
const CATALOG_MAX_BYTES: usize = 262_144;
4040
const NODE_TEST_CONCURRENCY: usize = 4;
41-
const BACKGROUND_HEALTHY_NODE_TARGET: usize = 10;
42-
const BACKGROUND_HEALTHY_NODE_MINIMUM: usize = 5;
41+
const AUTO_POOL_NODE_TARGET: usize = 10;
4342
const HEALTH_CHECK_INTERVALS: [u32; 6] = [
4443
0,
4544
60,
@@ -63,7 +62,7 @@ struct CatalogUpdate {
6362
recovery_applied_at: Option<chrono::DateTime<Utc>>,
6463
}
6564

66-
struct BackgroundNodeTestResult {
65+
struct NodePoolTestResult {
6766
tested: Vec<(String, HealthSummary)>,
6867
pool_ids: HashSet<String>,
6968
}
@@ -237,7 +236,7 @@ impl AppCore {
237236
*self.full_node_test_result.lock() = None;
238237
let nodes = self.nodes()?;
239238
let old_health = self.health()?;
240-
let result = test_background_node_pool(&nodes, &old_health, test, |_, _| {})?;
239+
let result = test_node_pool(&nodes, &old_health, test, |_, _| {})?;
241240
self.persist_test_results(
242241
result.tested,
243242
AutomaticPoolUpdate::Replace(result.pool_ids),
@@ -564,7 +563,7 @@ impl AppCore {
564563
let _run = self
565564
.full_node_test_lock
566565
.try_lock()
567-
.ok_or_else(|| "全量线路检测正在进行,请稍后再试".to_string())?;
566+
.ok_or_else(|| "线路检测正在进行,请稍后再试".to_string())?;
568567
self.execute_full_node_test_locked(&on_progress)
569568
}
570569

@@ -614,14 +613,10 @@ impl AppCore {
614613
where
615614
F: Fn(usize, usize) + Sync,
616615
{
617-
let nodes: Vec<NodeDefinition> = self
618-
.nodes()?
619-
.into_iter()
620-
.filter(|node| node.enabled)
621-
.collect();
616+
let nodes = self.nodes()?;
622617
let old_health = self.health()?;
623-
let tested = test_nodes_bounded(&nodes, &old_health, git::test_node, on_progress)?;
624-
self.persist_test_results(tested, AutomaticPoolUpdate::Rebuild)
618+
let result = test_node_pool(&nodes, &old_health, git::test_node, on_progress)?;
619+
self.persist_test_results(result.tested, AutomaticPoolUpdate::Rebuild)
625620
}
626621

627622
fn test_background_nodes_locked<F>(&self, on_progress: &F) -> Result<Vec<NodeEntry>, String>
@@ -630,7 +625,7 @@ impl AppCore {
630625
{
631626
let nodes = self.nodes()?;
632627
let old_health = self.health()?;
633-
let result = test_background_node_pool(&nodes, &old_health, git::test_node, on_progress)?;
628+
let result = test_node_pool(&nodes, &old_health, git::test_node, on_progress)?;
634629
self.persist_test_results(result.tested, AutomaticPoolUpdate::Replace(result.pool_ids))
635630
}
636631

@@ -689,7 +684,7 @@ impl AppCore {
689684
.is_some_and(|summary| summary.in_auto_pool && is_usable_health(summary))
690685
})
691686
.count()
692-
< BACKGROUND_HEALTHY_NODE_MINIMUM)
687+
< AUTO_POOL_NODE_TARGET)
693688
}
694689

695690
fn resume_acceleration_after_catalog_refresh(
@@ -1430,23 +1425,34 @@ fn select_auto_pool(
14301425
}
14311426
}
14321427
for (node, _) in candidates {
1433-
if selected.len() == BACKGROUND_HEALTHY_NODE_TARGET {
1428+
if selected.len() == AUTO_POOL_NODE_TARGET {
14341429
break;
14351430
}
14361431
selected.insert(node.id.clone());
14371432
}
14381433
selected
14391434
}
14401435

1441-
fn test_background_node_pool<F>(
1436+
fn test_node_pool<F>(
14421437
nodes: &[NodeDefinition],
14431438
old_health: &HashMap<String, HealthSummary>,
14441439
test: F,
14451440
on_progress: impl Fn(usize, usize) + Sync,
1446-
) -> Result<BackgroundNodeTestResult, String>
1441+
) -> Result<NodePoolTestResult, String>
14471442
where
14481443
F: Fn(&NodeDefinition, &HealthSummary) -> HealthSummary + Sync,
14491444
{
1445+
let usable_count = Mutex::new(0usize);
1446+
on_progress(0, AUTO_POOL_NODE_TARGET);
1447+
let test_and_report = |node: &NodeDefinition, previous: &HealthSummary| {
1448+
let summary = test(node, previous);
1449+
if is_usable_health(&summary) {
1450+
let mut usable = usable_count.lock();
1451+
*usable += 1;
1452+
on_progress(*usable, AUTO_POOL_NODE_TARGET);
1453+
}
1454+
summary
1455+
};
14501456
let maintained: Vec<NodeDefinition> = nodes
14511457
.iter()
14521458
.filter(|node| {
@@ -1455,50 +1461,36 @@ where
14551461
.get(&node.id)
14561462
.is_some_and(|summary| summary.in_auto_pool)
14571463
})
1458-
.take(BACKGROUND_HEALTHY_NODE_TARGET)
1464+
.take(AUTO_POOL_NODE_TARGET)
14591465
.cloned()
14601466
.collect();
14611467
let selected: HashSet<String> = maintained.iter().map(|node| node.id.clone()).collect();
1462-
let mut tested = test_nodes_bounded(&maintained, old_health, &test, |completed, total| {
1463-
on_progress(completed, total)
1464-
})?;
1468+
let mut tested = test_nodes_bounded(&maintained, old_health, &test_and_report, |_, _| {})?;
14651469

1466-
let mut usable = tested
1467-
.iter()
1468-
.filter(|(_, summary)| is_usable_health(summary))
1469-
.count();
1470-
if usable >= BACKGROUND_HEALTHY_NODE_MINIMUM {
1470+
let mut usable = *usable_count.lock();
1471+
if usable >= AUTO_POOL_NODE_TARGET {
14711472
let pool_ids = tested
14721473
.iter()
14731474
.filter(|(_, summary)| is_usable_health(summary))
14741475
.map(|(node_id, _)| node_id.clone())
14751476
.collect();
1476-
return Ok(BackgroundNodeTestResult { tested, pool_ids });
1477+
return Ok(NodePoolTestResult { tested, pool_ids });
14771478
}
14781479

14791480
let discovery: Vec<NodeDefinition> = nodes
14801481
.iter()
1481-
.filter(|node| node.enabled && node.built_in && !selected.contains(&node.id))
1482+
.filter(|node| node.enabled && !selected.contains(&node.id))
14821483
.cloned()
14831484
.collect();
14841485
let mut cursor = 0;
1485-
let mut completed = tested.len();
1486-
while usable < BACKGROUND_HEALTHY_NODE_TARGET && cursor < discovery.len() {
1487-
let remaining = BACKGROUND_HEALTHY_NODE_TARGET - usable;
1486+
while usable < AUTO_POOL_NODE_TARGET && cursor < discovery.len() {
1487+
let remaining = AUTO_POOL_NODE_TARGET - usable;
14881488
let batch_len = NODE_TEST_CONCURRENCY
14891489
.min(remaining)
14901490
.min(discovery.len() - cursor);
14911491
let batch = &discovery[cursor..cursor + batch_len];
1492-
let total = completed + batch_len;
1493-
let batch_results = test_nodes_bounded(batch, old_health, &test, |batch_completed, _| {
1494-
on_progress(completed + batch_completed, total)
1495-
})?;
1496-
for (_, summary) in &batch_results {
1497-
if is_usable_health(summary) {
1498-
usable += 1;
1499-
}
1500-
}
1501-
completed += batch_results.len();
1492+
let batch_results = test_nodes_bounded(batch, old_health, &test_and_report, |_, _| {})?;
1493+
usable = *usable_count.lock();
15021494
tested.extend(batch_results);
15031495
cursor += batch_len;
15041496
}
@@ -1507,7 +1499,7 @@ where
15071499
.filter(|(_, summary)| is_usable_health(summary))
15081500
.map(|(node_id, _)| node_id.clone())
15091501
.collect();
1510-
Ok(BackgroundNodeTestResult { tested, pool_ids })
1502+
Ok(NodePoolTestResult { tested, pool_ids })
15111503
}
15121504

15131505
fn test_nodes_bounded<F>(
@@ -2059,7 +2051,7 @@ mod tests {
20592051
core.test_all_nodes_with_progress(|_, _| {})
20602052
.err()
20612053
.as_deref(),
2062-
Some("全量线路检测正在进行,请稍后再试")
2054+
Some("线路检测正在进行,请稍后再试")
20632055
);
20642056
drop(active);
20652057
assert!(core.full_node_test_lock.try_lock().is_some());
@@ -2272,11 +2264,10 @@ mod tests {
22722264
.collect();
22732265

22742266
let result =
2275-
test_background_node_pool(&nodes, &health, |_, previous| previous.clone(), |_, _| {})
2276-
.unwrap();
2267+
test_node_pool(&nodes, &health, |_, previous| previous.clone(), |_, _| {}).unwrap();
22772268

2278-
assert_eq!(result.tested.len(), BACKGROUND_HEALTHY_NODE_TARGET);
2279-
assert_eq!(result.pool_ids.len(), BACKGROUND_HEALTHY_NODE_TARGET);
2269+
assert_eq!(result.tested.len(), AUTO_POOL_NODE_TARGET);
2270+
assert_eq!(result.pool_ids.len(), AUTO_POOL_NODE_TARGET);
22802271
assert_eq!(
22812272
result
22822273
.tested
@@ -2290,7 +2281,7 @@ mod tests {
22902281
}
22912282

22922283
#[test]
2293-
fn background_health_check_discovers_until_ten_when_pool_falls_below_five() {
2284+
fn node_pool_detection_discovers_until_ten_from_a_partial_pool() {
22942285
let nodes: Vec<NodeDefinition> = (0..30)
22952286
.map(|index| NodeDefinition {
22962287
id: format!("node-{index}"),
@@ -2315,7 +2306,7 @@ mod tests {
23152306
})
23162307
.collect();
23172308

2318-
let result = test_background_node_pool(
2309+
let result = test_node_pool(
23192310
&nodes,
23202311
&health,
23212312
|_, _| HealthSummary {
@@ -2326,32 +2317,32 @@ mod tests {
23262317
)
23272318
.unwrap();
23282319

2329-
assert_eq!(result.tested.len(), BACKGROUND_HEALTHY_NODE_TARGET);
2330-
assert_eq!(result.pool_ids.len(), BACKGROUND_HEALTHY_NODE_TARGET);
2320+
assert_eq!(result.tested.len(), AUTO_POOL_NODE_TARGET);
2321+
assert_eq!(result.pool_ids.len(), AUTO_POOL_NODE_TARGET);
23312322
assert_eq!(
23322323
result
23332324
.tested
23342325
.iter()
23352326
.filter(|(_, summary)| is_usable_health(summary))
23362327
.count(),
2337-
BACKGROUND_HEALTHY_NODE_TARGET
2328+
AUTO_POOL_NODE_TARGET
23382329
);
23392330
}
23402331

23412332
#[test]
2342-
fn background_health_check_does_not_discover_when_five_nodes_remain_usable() {
2333+
fn node_pool_detection_fills_to_ten_with_custom_nodes() {
23432334
let nodes: Vec<NodeDefinition> = (0..20)
23442335
.map(|index| NodeDefinition {
23452336
id: format!("node-{index}"),
23462337
name: format!("Node {index}"),
23472338
rewrite_base: format!("https://proxy-{index}.example/https://github.com/"),
23482339
enabled: true,
2349-
built_in: true,
2340+
built_in: false,
23502341
})
23512342
.collect();
23522343
let health = nodes
23532344
.iter()
2354-
.take(BACKGROUND_HEALTHY_NODE_MINIMUM)
2345+
.take(5)
23552346
.map(|node| {
23562347
(
23572348
node.id.clone(),
@@ -2364,12 +2355,66 @@ mod tests {
23642355
})
23652356
.collect();
23662357

2367-
let result =
2368-
test_background_node_pool(&nodes, &health, |_, previous| previous.clone(), |_, _| {})
2369-
.unwrap();
2358+
let result = test_node_pool(
2359+
&nodes,
2360+
&health,
2361+
|_, _| HealthSummary {
2362+
status: NodeStatus::Available,
2363+
..HealthSummary::default()
2364+
},
2365+
|_, _| {},
2366+
)
2367+
.unwrap();
2368+
2369+
assert_eq!(result.tested.len(), AUTO_POOL_NODE_TARGET);
2370+
assert_eq!(result.pool_ids.len(), AUTO_POOL_NODE_TARGET);
2371+
}
23702372

2371-
assert_eq!(result.tested.len(), BACKGROUND_HEALTHY_NODE_MINIMUM);
2372-
assert_eq!(result.pool_ids.len(), BACKGROUND_HEALTHY_NODE_MINIMUM);
2373+
#[test]
2374+
fn node_pool_detection_continues_past_failures_and_stops_at_ten_usable_nodes() {
2375+
let nodes: Vec<NodeDefinition> = (0..30)
2376+
.map(|index| NodeDefinition {
2377+
id: format!("node-{index}"),
2378+
name: format!("Node {index}"),
2379+
rewrite_base: format!("https://proxy-{index}.example/https://github.com/"),
2380+
enabled: true,
2381+
built_in: true,
2382+
})
2383+
.collect();
2384+
2385+
let progress = Mutex::new(Vec::new());
2386+
let result = test_node_pool(
2387+
&nodes,
2388+
&HashMap::new(),
2389+
|node, _| HealthSummary {
2390+
status: if node
2391+
.id
2392+
.strip_prefix("node-")
2393+
.unwrap()
2394+
.parse::<usize>()
2395+
.unwrap()
2396+
% 2
2397+
== 0
2398+
{
2399+
NodeStatus::Available
2400+
} else {
2401+
NodeStatus::Unavailable
2402+
},
2403+
..HealthSummary::default()
2404+
},
2405+
|completed, total| progress.lock().push((completed, total)),
2406+
)
2407+
.unwrap();
2408+
2409+
assert_eq!(result.tested.len(), 19);
2410+
assert_eq!(result.pool_ids.len(), AUTO_POOL_NODE_TARGET);
2411+
assert_eq!(result.tested.last().unwrap().0, "node-18");
2412+
assert_eq!(
2413+
progress.into_inner(),
2414+
(0..=AUTO_POOL_NODE_TARGET)
2415+
.map(|completed| (completed, AUTO_POOL_NODE_TARGET))
2416+
.collect::<Vec<_>>()
2417+
);
23732418
}
23742419

23752420
#[test]
@@ -2413,7 +2458,7 @@ mod tests {
24132458
.map(|(node_id, _)| node_id.as_str())
24142459
.collect();
24152460

2416-
assert_eq!(pool_ids.len(), BACKGROUND_HEALTHY_NODE_TARGET);
2461+
assert_eq!(pool_ids.len(), AUTO_POOL_NODE_TARGET);
24172462
assert!(!pool_ids.contains("node-10"));
24182463

24192464
core.persist_test_results(
@@ -2428,7 +2473,7 @@ mod tests {
24282473
.values()
24292474
.filter(|summary| summary.in_auto_pool)
24302475
.count(),
2431-
BACKGROUND_HEALTHY_NODE_TARGET
2476+
AUTO_POOL_NODE_TARGET
24322477
);
24332478
}
24342479

@@ -2723,11 +2768,15 @@ mod tests {
27232768
.collect::<Vec<_>>()
27242769
);
27252770
let progress = progress.into_inner();
2726-
let total = core.nodes().unwrap().len();
2771+
let usable = owner_result
2772+
.iter()
2773+
.filter(|node| is_usable_health(&node.health))
2774+
.count();
2775+
assert!(usable <= AUTO_POOL_NODE_TARGET);
27272776
assert_eq!(
27282777
progress,
2729-
(0..=total)
2730-
.map(|completed| (completed, total))
2778+
(0..=usable)
2779+
.map(|completed| (completed, AUTO_POOL_NODE_TARGET))
27312780
.collect::<Vec<_>>()
27322781
);
27332782

0 commit comments

Comments
 (0)