refactor: Enhance node switching logic to prioritize unused active nodes (#2639)
David Adegoke committed
Nov 8, 2025 at 21:11 UTC
ddf6043ed9175b88663885077f865db626e0cdb3
1 file changed
+33
-16
lib/core/node_switching_service.dart
+33
-16
@@ -22,7 +22,6 @@ class NodeSwitchingService {
22
// Cooldown period between node switching attempts (in seconds)
23
static const int _nodeSwitchingCooldownSeconds = 15;
24
25
- // State to manage switching attempts and cooldown
25
int _switchingAttempts = 0;
26
DateTime? _lastSwitchingAttempt;
27
bool _hasExhaustedAllNodes = false;
@@ -38,7 +37,6 @@ class NodeSwitchingService {
37
final SettingsStore settingsStore;
38
final Box<Node> nodeSource;
39
41
- // Track used nodes to cycle through all trusted nodes
40
final Map<WalletType, List<dynamic>> _usedNodeKeys = {};
41
42
void startHealthCheckTimer() {
@@ -85,7 +83,6 @@ class NodeSwitchingService {
83
}
84
85
try {
88
- // Check network connectivity first
86
final connectivityResult = await Connectivity().checkConnectivity();
87
if (connectivityResult == ConnectivityResult.none) {
88
printV('No network connectivity detected. Skipping node health check.');
@@ -108,6 +105,26 @@ class NodeSwitchingService {
105
}
106
}
107
108
+ /// Find an active node from the provided list, checking only unused nodes
109
+ /// Marks inactive nodes as used to avoid retrying them
110
+ Future<Node?> _findActiveNode(
111
+ List<Node> nodes,
112
+ WalletType walletType,
113
+ ) async {
114
+ for (final node in nodes) {
115
+ if (!_usedNodeKeys[walletType]!.contains(node.key)) {
116
+ final isActive = await node.requestNode();
117
+ if (isActive) {
118
+ return node;
119
+ } else {
120
+ printV('Node ${node.uriRaw} is not active. Marking as used.');
121
+ _usedNodeKeys[walletType]!.add(node.key);
122
+ }
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+
128
/// Switch to the next available trusted node
129
Future<void> _switchToNextTrustedNode() async {
130
_isSwitching = true;
@@ -143,14 +160,8 @@ class NodeSwitchingService {
160
_usedNodeKeys[walletType]!.add(currentNode.key);
161
}
162
146
- // Get next unused trusted node from the list
147
- Node? nextNode;
148
- for (final node in trustedNodes) {
149
- if (!_usedNodeKeys[walletType]!.contains(node.key)) {
150
- nextNode = node;
151
- break;
152
- }
153
- }
163
+ // Try to find an active unused node
164
+ Node? nextNode = await _findActiveNode(trustedNodes, walletType);
165
166
// If all trusted nodes have been used, check if we should reset
167
if (nextNode == null) {
@@ -160,16 +171,22 @@ class NodeSwitchingService {
171
if (_switchingAttempts < _maxNodeSwitchingAttempts) {
172
printV('Resetting used nodes list and trying again');
173
_usedNodeKeys[walletType]!.clear();
163
- nextNode = trustedNodes.first;
164
- } else {
165
- printV('Maximum attempts reached. No more node switching.');
174
+ // Try again with cleared used list
175
+ nextNode = await _findActiveNode(trustedNodes, walletType);
176
+ }
177
+
178
+ // If still no active node found, we give up
179
+ if (nextNode == null) {
180
+ printV('No active nodes available for switching after checking all nodes.');
181
_hasExhaustedAllNodes = true;
182
return;
183
}
184
}
185
171
- // Add the next node to used list
172
- _usedNodeKeys[walletType]!.add(nextNode.key);
186
+ // Ensure the selected node is marked as used
187
+ if (!_usedNodeKeys[walletType]!.contains(nextNode.key)) {
188
+ _usedNodeKeys[walletType]!.add(nextNode.key);
189
+ }
190
191
printV(
192
'Switching from ${currentNode.uriRaw} to ${nextNode.uriRaw} (attempt $_switchingAttempts/$_maxNodeSwitchingAttempts)');