8a66b9086a
- Started from ac75faa (initial E4B-MarkBase integration)
- Kept Sources/ (all engine code) + Package.swift + .gitignore
- Removed all ad-hoc tests, documentation, scripts, Python files
- Added Tests/00_Unit/ (MathTest, TokenizerTest, SamplerTest)
- Added .gitea/workflows/ci.yaml (build + unit tests + lint)
- Added Scripts/check_resources.sh (memory-aware test runner)
- Added Tests/Manifest.json (resource requirements for all tests)
- Focus: 4-bit quantized models only
93 lines
2.7 KiB
Swift
93 lines
2.7 KiB
Swift
import Foundation
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// Load Balancer for Cross-Device Communication
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
/// Load balancing strategies
|
|
public enum LoadBalancingStrategy: Sendable {
|
|
case roundRobin
|
|
case leastLoaded
|
|
case random
|
|
case geographic
|
|
}
|
|
|
|
/// Load balancer
|
|
public final class LoadBalancer: @unchecked Sendable {
|
|
private var nodes: [DeviceNode] = []
|
|
private var currentIndex: Int = 0
|
|
private let strategy: LoadBalancingStrategy
|
|
private let lock = NSLock()
|
|
|
|
public init(strategy: LoadBalancingStrategy = .roundRobin) {
|
|
self.strategy = strategy
|
|
}
|
|
|
|
/// Add a node
|
|
public func addNode(_ node: DeviceNode) {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
nodes.append(node)
|
|
}
|
|
|
|
/// Remove a node
|
|
public func removeNode(id: String) {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
nodes.removeAll { $0.id == id }
|
|
}
|
|
|
|
/// Get next node based on strategy
|
|
public func getNextNode() -> DeviceNode? {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
|
|
let healthyNodes = nodes.filter { $0.status == .healthy }
|
|
guard !healthyNodes.isEmpty else { return nil }
|
|
|
|
switch strategy {
|
|
case .roundRobin:
|
|
let node = healthyNodes[currentIndex % healthyNodes.count]
|
|
currentIndex += 1
|
|
return node
|
|
|
|
case .leastLoaded:
|
|
return healthyNodes.min { $0.load < $1.load }
|
|
|
|
case .random:
|
|
return healthyNodes.randomElement()
|
|
|
|
case .geographic:
|
|
// Simplified: return node with lowest latency
|
|
return healthyNodes.first
|
|
}
|
|
}
|
|
|
|
/// Update node status
|
|
public func updateNodeStatus(id: String, status: DeviceStatus, load: Double? = nil) {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
|
|
if let index = nodes.firstIndex(where: { $0.id == id }) {
|
|
var node = nodes[index]
|
|
node.status = status
|
|
if let load = load {
|
|
node.load = load
|
|
}
|
|
nodes[index] = node
|
|
}
|
|
}
|
|
|
|
/// Get all nodes
|
|
public func getNodes() -> [DeviceNode] {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
return nodes
|
|
}
|
|
|
|
/// Get healthy nodes count
|
|
public var healthyNodesCount: Int {
|
|
nodes.filter { $0.status == .healthy }.count
|
|
}
|
|
}
|