v2: Initial clean branch with unit tests + CI/CD pipeline
- 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
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user