huddle-cluster 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rahad Bhuiya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: huddle-cluster
|
|
3
|
+
Version: 1.3.1
|
|
4
|
+
Summary: A penguin-inspired self-organizing server load balancer with adaptive thermal eviction
|
|
5
|
+
Home-page: https://github.com/rahadbhuiya/HuddleCluster
|
|
6
|
+
Author: Rahad Bhuiya
|
|
7
|
+
Author-email: Rahad Bhuiya <rahadbhuiya@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: load-balancer,load-balancing,distributed-systems,bio-inspired,penguin,adaptive,anomaly-detection
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
18
|
+
Classifier: Topic :: System :: Networking
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Project-URL: Homepage, https://github.com/rahadbhuiya/HuddleCluster
|
|
22
|
+
Project-URL: Repository, https://github.com/rahadbhuiya/HuddleCluster
|
|
23
|
+
Project-URL: Bug Tracker, https://github.com/rahadbhuiya/HuddleCluster/issues
|
|
24
|
+
|
|
25
|
+
# HuddleCluster
|
|
26
|
+
|
|
27
|
+
A penguin-inspired, self-organizing server load balancer with adaptive thermal eviction.
|
|
28
|
+
|
|
29
|
+
**Author:** Rahad Bhuiya
|
|
30
|
+
**Version:** 1.3.0
|
|
31
|
+
**License:** MIT
|
|
32
|
+
**Paper:** [HuddleCluster: A Penguin-Inspired Self-Organizing Load Balancer with Adaptive Thermal Eviction](https://github.com/rahadbhuiya/HuddleCluster/blob/main/docs/HuddleCluster_Paper.pdf)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## The Idea
|
|
37
|
+
|
|
38
|
+
Emperor Penguins survive Antarctic winters by forming huddles. Penguins on the cold outer edge
|
|
39
|
+
push inward toward warmth, while those in the center gradually rotate outward to rest — with no
|
|
40
|
+
central coordinator, only local temperature thresholds.
|
|
41
|
+
|
|
42
|
+
HuddleCluster maps this directly to server scheduling:
|
|
43
|
+
|
|
44
|
+
- **Inner ring** — active servers handling requests (warm)
|
|
45
|
+
- **Outer ring** — resting servers recovering from load (cool)
|
|
46
|
+
- **Temperature** — a composite EMA score derived from relative latency anomaly, CPU, memory, connections, and error rate
|
|
47
|
+
- **Rotation** — overheated servers evict to outer ring; cooled servers return to inner ring automatically
|
|
48
|
+
|
|
49
|
+
The key innovation is **relative latency anomaly scoring**: instead of comparing a server's
|
|
50
|
+
latency to an absolute threshold, HuddleCluster compares each server to the cluster-wide
|
|
51
|
+
median. A server 3x slower than its peers is evicted regardless of whether the baseline is
|
|
52
|
+
10 ms or 300 ms.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Benchmark Results
|
|
57
|
+
|
|
58
|
+
### Simulated Benchmark (10 trials, mean +/- std, Welch's t-test)
|
|
59
|
+
|
|
60
|
+
| Scenario / Metric | Round Robin | Least Conn | HuddleCluster | p-value |
|
|
61
|
+
|---|---|---|---|---|
|
|
62
|
+
| **Normal Load** | | | | |
|
|
63
|
+
| P50 (ms) | 21.5 +/- 0.2 | 21.2 +/- 0.3 | 21.0 +/- 0.2 | 0.000* |
|
|
64
|
+
| P95 (ms) | 29.6 +/- 0.3 | 28.8 +/- 0.4 | 28.6 +/- 0.6 | 0.001* |
|
|
65
|
+
| Avg (ms) | 21.4 +/- 0.1 | 21.1 +/- 0.2 | 21.0 +/- 0.2 | 0.000* |
|
|
66
|
+
| Fairness (Gini) | 0.000 | 0.067 | **0.000** | -- |
|
|
67
|
+
| **Slow Server (5x at halfway)** | | | | |
|
|
68
|
+
| P95 (ms) | 63.2 +/- 1.0 | 61.7 +/- 1.1 | 55.1 +/- 10.6 | 0.039* |
|
|
69
|
+
| Avg (ms) | 20.1 +/- 0.2 | 19.7 +/- 0.2 | 19.6 +/- 0.4 | 0.002* |
|
|
70
|
+
| **Server Failure (crash at halfway)** | | | | |
|
|
71
|
+
| P95 (ms) | 500.0 +/- 0.0 | 500.0 +/- 0.0 | **23.9 +/- 0.5** | 0.000* |
|
|
72
|
+
| Avg (ms) | 53.4 +/- 0.2 | 229.7 +/- 1.4 | **29.7 +/- 0.1** | 0.000* |
|
|
73
|
+
|
|
74
|
+
*statistically significant (p < 0.05)*
|
|
75
|
+
|
|
76
|
+
### Real HTTP Benchmark (6 FastAPI servers, loopback)
|
|
77
|
+
|
|
78
|
+
| Scenario / Metric | Round Robin | Least Conn | HuddleCluster | vs RR |
|
|
79
|
+
|---|---|---|---|---|
|
|
80
|
+
| **Normal Load** | | | | |
|
|
81
|
+
| P95 (ms) | 88.6 | 85.3 | 74.3 | +16.2% |
|
|
82
|
+
| Avg (ms) | 51.8 | 48.3 | 46.1 | +11.0% |
|
|
83
|
+
| **Slow Server (5x)** | | | | |
|
|
84
|
+
| Avg (ms) | 55.2 | 52.1 | 53.4 | +3.4% |
|
|
85
|
+
| **Server Failure** | | | | |
|
|
86
|
+
| P95 (ms) | 5,026.9 | 5,027.9 | **85.6** | +98.3% |
|
|
87
|
+
| Avg (ms) | 429.7 | 414.0 | **181.5** | +57.7% |
|
|
88
|
+
|
|
89
|
+
### Industry Baseline (NGINX vs HuddleCluster, Docker)
|
|
90
|
+
|
|
91
|
+
Containerised benchmark: 6 FastAPI upstream servers, Docker bridge network,
|
|
92
|
+
NGINX round-robin and NGINX least-connections as baselines.
|
|
93
|
+
|
|
94
|
+
| Scenario / Metric | NGINX RR | NGINX LC | HuddleCluster | vs NGINX RR |
|
|
95
|
+
|---|---|---|---|---|
|
|
96
|
+
| **Normal Load** | | | | |
|
|
97
|
+
| P50 (ms) | 28.4 | 27.5 | **20.5** | +28.0% |
|
|
98
|
+
| P95 (ms) | 55.1 | 39.3 | **33.4** | +39.4% |
|
|
99
|
+
| Avg (ms) | 29.1 | 26.4 | **21.4** | +26.5% |
|
|
100
|
+
| **Slow Server (5x)** | | | | |
|
|
101
|
+
| P50 (ms) | 25.3 | 25.3 | **19.8** | +21.6% |
|
|
102
|
+
| P95 (ms) | 38.9 | 42.8 | **33.6** | +13.6% |
|
|
103
|
+
| Avg (ms) | 25.1 | 25.8 | **20.5** | +18.4% |
|
|
104
|
+
| **Server Failure** | | | | |
|
|
105
|
+
| P95 (ms) | 45.9 | 41.9 | **29.7** | +35.3% |
|
|
106
|
+
| Avg (ms) | 25.9 | 25.6 | **20.8** | +19.4% |
|
|
107
|
+
|
|
108
|
+
Note: admin endpoint injection was not available in this Docker run
|
|
109
|
+
(upstream servers on internal network only). Results reflect
|
|
110
|
+
HuddleCluster's thermal rotation advantage without injected failures.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
cd benchmarks/
|
|
114
|
+
docker compose up -d --build
|
|
115
|
+
python benchmark_industry.py
|
|
116
|
+
docker compose down
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Overhead
|
|
120
|
+
|
|
121
|
+
| Measurement | Value |
|
|
122
|
+
|---|---|
|
|
123
|
+
| RR get_server() | 0.277 us |
|
|
124
|
+
| HC get_server() | 0.295 us (1.07x over RR) |
|
|
125
|
+
| HC get_server() + record_latency() | 10.7 us |
|
|
126
|
+
| Peak memory (20 servers) | 28.3 KB |
|
|
127
|
+
| Slow-server detection speed | 36 requests avg (range 35-40) |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Quick Start
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pip install -e .
|
|
135
|
+
# with benchmark dependencies:
|
|
136
|
+
pip install -e ".[benchmark]"
|
|
137
|
+
# with FastAPI integration:
|
|
138
|
+
pip install -e ".[fastapi]"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from huddle_cluster import create_cluster
|
|
143
|
+
import time, requests
|
|
144
|
+
|
|
145
|
+
cluster = create_cluster([
|
|
146
|
+
("s1", "10.0.0.1", 8080),
|
|
147
|
+
("s2", "10.0.0.2", 8080),
|
|
148
|
+
("s3", "10.0.0.3", 8080),
|
|
149
|
+
])
|
|
150
|
+
cluster.start()
|
|
151
|
+
|
|
152
|
+
# Route a request with latency feedback
|
|
153
|
+
server = cluster.get_server()
|
|
154
|
+
t0 = time.perf_counter()
|
|
155
|
+
response = requests.get(f"http://{server.host}:{server.port}/api")
|
|
156
|
+
cluster.record_latency(server, (time.perf_counter() - t0) * 1000)
|
|
157
|
+
|
|
158
|
+
# Or use the context manager (auto-records latency)
|
|
159
|
+
with cluster.get_server_context() as server:
|
|
160
|
+
response = requests.get(f"http://{server.host}:{server.port}/api")
|
|
161
|
+
|
|
162
|
+
print(cluster.health_report())
|
|
163
|
+
cluster.stop()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## v1.3.0 Features
|
|
169
|
+
|
|
170
|
+
### Weighted Server Capacity
|
|
171
|
+
|
|
172
|
+
Servers with higher weight tolerate more load before eviction. Useful for
|
|
173
|
+
heterogeneous clusters where some instances are larger than others.
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
cluster = create_cluster([
|
|
177
|
+
("s1", "10.0.0.1", 8080), # weight=1.0 (default)
|
|
178
|
+
("s2", "10.0.0.2", 8080, 2.0), # weight=2.0 -- needs 2x heat to evict
|
|
179
|
+
("s3", "10.0.0.3", 8080, 0.5), # weight=0.5 -- evicts sooner
|
|
180
|
+
])
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Cold Start Protection
|
|
184
|
+
|
|
185
|
+
New servers warm up in the outer ring before handling traffic. Prevents
|
|
186
|
+
request spikes on fresh instances that have not yet warmed their caches
|
|
187
|
+
or JIT compilers.
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
cluster = HuddleCluster(cold_start_sec=30.0)
|
|
191
|
+
# Any server added will stay in outer ring for 30 seconds
|
|
192
|
+
# regardless of force_inner=True
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Absolute Latency Floor
|
|
196
|
+
|
|
197
|
+
Guards against majority degradation where the relative anomaly score breaks
|
|
198
|
+
down (when the cluster median itself rises above acceptable levels).
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
cluster = HuddleCluster(absolute_latency_floor_ms=500.0)
|
|
202
|
+
# Any server with avg latency > 500ms is evicted regardless of relative score
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Adaptive Thresholds
|
|
206
|
+
|
|
207
|
+
Heat and cool thresholds auto-adjust based on cluster P95 latency history.
|
|
208
|
+
Thresholds loosen under sustained load (to avoid over-eviction) and tighten
|
|
209
|
+
when the cluster is healthy (for faster anomaly detection).
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
cluster = HuddleCluster(adaptive_thresholds=True)
|
|
213
|
+
# heat_threshold and cool_threshold update automatically
|
|
214
|
+
# Check current values via cluster.health_report()["heat_threshold"]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Prometheus Metrics Exporter
|
|
218
|
+
|
|
219
|
+
Expose cluster state as Prometheus metrics for Grafana dashboards.
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# FastAPI example
|
|
223
|
+
from fastapi import FastAPI
|
|
224
|
+
from fastapi.responses import PlainTextResponse
|
|
225
|
+
|
|
226
|
+
app = FastAPI()
|
|
227
|
+
|
|
228
|
+
@app.get("/metrics", response_class=PlainTextResponse)
|
|
229
|
+
def metrics():
|
|
230
|
+
return cluster.prometheus_metrics()
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Metrics exposed: `huddle_server_temperature`, `huddle_server_avg_latency_ms`,
|
|
234
|
+
`huddle_server_anomaly_score`, `huddle_server_rotations_total`,
|
|
235
|
+
`huddle_cluster_inner_count`, `huddle_cluster_fairness_gini`,
|
|
236
|
+
`huddle_cluster_heat_threshold`, `huddle_cluster_p95_latency_ms`.
|
|
237
|
+
|
|
238
|
+
### Gossip Protocol (Distributed Deployments)
|
|
239
|
+
|
|
240
|
+
Share temperature state between multiple HuddleCluster instances via UDP
|
|
241
|
+
multicast. Each node broadcasts its inner-ring server states; peers receive
|
|
242
|
+
them as advisory signals.
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
from huddle_cluster import GossipAgent, create_cluster
|
|
246
|
+
|
|
247
|
+
agent = GossipAgent(node_id="node-1", gossip_port=9999)
|
|
248
|
+
cluster = create_cluster([...], gossip_agent=agent)
|
|
249
|
+
cluster.start()
|
|
250
|
+
|
|
251
|
+
# See peer states
|
|
252
|
+
peers = agent.peer_states()
|
|
253
|
+
# {"node-2": [{"id": "s0", "temp": 0.12, "avg_ms": 15.3, "pos": "inner"}]}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Note: gossip is best-effort UDP multicast. The cluster remains fully
|
|
257
|
+
functional without gossip -- it is purely additive.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## File Structure
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
HuddleCluster/
|
|
265
|
+
|
|
|
266
|
+
|-- huddle_cluster.py # Core library v1.3.0 (zero runtime dependencies)
|
|
267
|
+
|-- __init__.py # Package exports
|
|
268
|
+
|-- pyproject.toml # pip install support
|
|
269
|
+
|-- requirements.txt # Optional dependencies by feature
|
|
270
|
+
|-- LICENSE
|
|
271
|
+
|
|
|
272
|
+
|-- benchmarks/
|
|
273
|
+
| |-- benchmark.py # Simulated 4-scenario benchmark
|
|
274
|
+
| |-- benchmark_statistical.py # 10-trial statistical benchmark with CI
|
|
275
|
+
| |-- benchmark_http.py # Real HTTP benchmark (6 FastAPI servers)
|
|
276
|
+
| |-- benchmark_industry.py # NGINX vs HuddleCluster (Docker)
|
|
277
|
+
| |-- upstream_server.py # FastAPI upstream server
|
|
278
|
+
| |-- docker-compose.yml # 6 upstream servers + 2 NGINX instances
|
|
279
|
+
| |-- nginx/
|
|
280
|
+
| | |-- nginx_rr.conf # NGINX round-robin config
|
|
281
|
+
| | |-- nginx_lc.conf # NGINX least-connections config
|
|
282
|
+
| |-- run_http_benchmark.bat # Windows one-click runner
|
|
283
|
+
|
|
|
284
|
+
|-- tests/
|
|
285
|
+
| |-- test_rotation.py # Rotation, eviction, feedback loop (45 tests)
|
|
286
|
+
| |-- test_fairness.py # Fairness and Gini tests
|
|
287
|
+
| |-- test_stress.py # Concurrent load tests
|
|
288
|
+
| |-- conftest.py # Shared fixtures
|
|
289
|
+
|
|
|
290
|
+
|-- examples/
|
|
291
|
+
| |-- fastapi_example.py # FastAPI reverse proxy integration
|
|
292
|
+
| |-- simulation.py # Terminal simulation
|
|
293
|
+
| |-- HuddleSimulation.jsx # React visual simulation
|
|
294
|
+
|
|
|
295
|
+
|-- docs/
|
|
296
|
+
|-- diagrams/
|
|
297
|
+
|-- architecture_diagram.png # Dual-ring architecture
|
|
298
|
+
|-- temperature_lifecycle.png # State machine + weight composition
|
|
299
|
+
|-- rotation_flowchart.png # Rotation algorithm flowchart
|
|
300
|
+
|-- generate_diagrams.py # Regenerate diagrams
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## How It Works
|
|
306
|
+
|
|
307
|
+
### Temperature Formula
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
raw(s) = 0.70 x anomaly(s) # relative latency vs cluster median
|
|
311
|
+
+ 0.10 x cpu(s) # CPU usage [0,1]
|
|
312
|
+
+ 0.10 x conn(s) # active connections / 1000, clamped [0,1]
|
|
313
|
+
+ 0.05 x mem(s) # memory usage [0,1]
|
|
314
|
+
+ 0.05 x err(s) # error rate [0,1]
|
|
315
|
+
|
|
316
|
+
T(s) = alpha x raw(s) + (1 - alpha) x T(s) [EMA, default alpha=0.60]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Relative Latency Anomaly
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
anomaly(s) = clamp( (avg_ms(s) / median_ms(inner_ring) - 1) / 2, 0, 1 )
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
| Server / Cluster Median | Ratio | Anomaly Score | Cycles to eviction |
|
|
326
|
+
|---|---|---|---|
|
|
327
|
+
| 12 ms / 12 ms | 1.0x (normal) | 0.00 | Never |
|
|
328
|
+
| 24 ms / 12 ms | 2.0x (warm) | 0.50 | ~8 cycles |
|
|
329
|
+
| 36 ms / 12 ms | 3.0x (hot) | 1.00 | ~3 cycles |
|
|
330
|
+
| 60 ms / 12 ms | 5.0x (degraded) | 1.00 (clamped) | ~3 cycles |
|
|
331
|
+
|
|
332
|
+
### Rotation Rules
|
|
333
|
+
|
|
334
|
+
1. **Eviction** — inner server with T >= 0.55 moves to outer ring. Capped at max(1, |inner|/3) per cycle (thundering-herd prevention).
|
|
335
|
+
2. **Promotion** — coolest outer server with T <= 0.30 and sufficient dwell time moves to inner ring (flapping prevention).
|
|
336
|
+
3. **Health eviction** — server with is_healthy=False is evicted immediately regardless of temperature.
|
|
337
|
+
4. **Emergency fallback** — if inner ring drops below min_inner, the globally coolest server is promoted unconditionally.
|
|
338
|
+
|
|
339
|
+
### Failure-Mode Bounds
|
|
340
|
+
|
|
341
|
+
**Median robustness**: up to floor((n-1)/2) simultaneous server degradations can be
|
|
342
|
+
detected correctly. If k >= n/2 servers degrade simultaneously, the median baseline rises
|
|
343
|
+
and anomaly detection weakens — a documented boundary condition.
|
|
344
|
+
|
|
345
|
+
**Oscillation bound**: a server cannot oscillate faster than
|
|
346
|
+
`rotation_cooldown_sec + min_outer_dwell_sec` per cycle (default: 15 seconds minimum).
|
|
347
|
+
EMA smoothing requires at least 20 consecutive anomalous readings before a healthy server
|
|
348
|
+
(raw < 0.10) is evicted.
|
|
349
|
+
|
|
350
|
+
**Worst-case eviction rate**: at most max(1, floor(|inner|/3)) evictions per rotation
|
|
351
|
+
cycle. With default settings, the inner ring never drops below min_inner=2 active servers.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Configuration
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
cluster = HuddleCluster(
|
|
359
|
+
heat_threshold = 0.55, # Evict above this temperature
|
|
360
|
+
cool_threshold = 0.30, # Promote below this temperature
|
|
361
|
+
min_inner_size = 2, # Minimum active servers
|
|
362
|
+
max_inner_size = 5, # Maximum active servers
|
|
363
|
+
rotation_cooldown_sec = 5.0, # Minimum seconds between evictions per server
|
|
364
|
+
min_outer_dwell_sec = 10.0, # Minimum rest time before re-entry
|
|
365
|
+
ema_alpha = 0.60, # Temperature smoothing (higher = faster reaction)
|
|
366
|
+
# v1.3.0 new parameters
|
|
367
|
+
absolute_latency_floor_ms = None, # Evict any server above this absolute latency
|
|
368
|
+
cold_start_sec = 0.0, # New servers warm up in outer ring for this long
|
|
369
|
+
adaptive_thresholds = False, # Auto-adjust thresholds from cluster P95 history
|
|
370
|
+
gossip_agent = None, # GossipAgent for distributed deployments
|
|
371
|
+
metrics_updater = None, # Optional: fn(server) -> updates server.metrics
|
|
372
|
+
on_rotation = None, # Optional: fn(RotationEvent) -> called on rotation
|
|
373
|
+
)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Parameter Sensitivity (P95 ms, slow-server scenario)
|
|
377
|
+
|
|
378
|
+
| heat_threshold \ alpha | alpha=0.3 | alpha=0.6 (default) | alpha=0.9 |
|
|
379
|
+
|---|---|---|---|
|
|
380
|
+
| 0.45 (aggressive) | 38.2 | 31.4 | 29.1 |
|
|
381
|
+
| **0.55 (default)** | 52.3 | **32.0** | 30.8 |
|
|
382
|
+
| 0.65 (conservative) | 74.1 | 58.6 | 41.2 |
|
|
383
|
+
|
|
384
|
+
Default (heat=0.55, alpha=0.60) balances detection speed and eviction stability.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Running Tests
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
# All 45 tests
|
|
392
|
+
python -m unittest tests/test_rotation.py tests/test_fairness.py tests/test_stress.py
|
|
393
|
+
|
|
394
|
+
# With pytest
|
|
395
|
+
pip install ".[dev]"
|
|
396
|
+
pytest tests/ -v
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Running Benchmarks
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
cd benchmarks/
|
|
405
|
+
|
|
406
|
+
# Simulated (4 scenarios, ~2 min)
|
|
407
|
+
python benchmark.py
|
|
408
|
+
|
|
409
|
+
# Statistical (10 trials, p-values, CI, ~6 min)
|
|
410
|
+
pip install scipy matplotlib numpy
|
|
411
|
+
python benchmark_statistical.py
|
|
412
|
+
|
|
413
|
+
# Real HTTP (6 FastAPI servers, ~3 min)
|
|
414
|
+
pip install fastapi uvicorn httpx matplotlib numpy
|
|
415
|
+
python benchmark_http.py # Linux/Mac
|
|
416
|
+
run_http_benchmark.bat # Windows
|
|
417
|
+
|
|
418
|
+
# Industry baseline: NGINX vs HuddleCluster (requires Docker)
|
|
419
|
+
docker compose up -d
|
|
420
|
+
python benchmark_industry.py
|
|
421
|
+
docker compose down
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Known Limitations
|
|
427
|
+
|
|
428
|
+
- **Uniform burst load**: when all servers are equally stressed, relative anomaly scores are near zero and no eviction fires. An absolute latency floor is planned.
|
|
429
|
+
- **Majority degradation**: if more than half the inner-ring servers degrade simultaneously, the median baseline rises. Use `absolute_latency_floor_ms` as a secondary guard in this scenario.
|
|
430
|
+
- **Single-process**: temperature state is not shared across hosts. A gossip-protocol extension is planned.
|
|
431
|
+
- **Loopback benchmarks**: all HTTP benchmarks use localhost. Wide-area production validation is future work.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Roadmap
|
|
436
|
+
|
|
437
|
+
- [x] Latency feedback loop (record_latency, get_server_context) — v1.1.0
|
|
438
|
+
- [x] Relative latency anomaly scoring (median baseline) — v1.2.0
|
|
439
|
+
- [x] Inner-ring fairness metric (Gini) — v1.2.0
|
|
440
|
+
- [x] Tunable EMA alpha — v1.2.0
|
|
441
|
+
- [x] Statistical benchmark (10 trials, Welch's t-test, 95% CI) — v1.2.0
|
|
442
|
+
- [x] Real HTTP benchmark (FastAPI upstream servers) — v1.2.0
|
|
443
|
+
- [x] Industry baseline benchmark (NGINX, Docker) — v1.2.0
|
|
444
|
+
- [x] Failure-mode bounds (median robustness, oscillation, eviction rate) — v1.2.0
|
|
445
|
+
- [x] Adaptive thresholds (auto-adjust heat/cool from cluster P95 history) -- v1.3.0
|
|
446
|
+
- [x] Weighted server capacity (weight= param on Server/create_cluster) -- v1.3.0
|
|
447
|
+
- [x] Cold start protection (cold_start_sec= param) -- v1.3.0
|
|
448
|
+
- [x] Prometheus metrics exporter (cluster.prometheus_metrics()) -- v1.3.0
|
|
449
|
+
- [x] Distributed temperature sharing (GossipAgent, UDP multicast) -- v1.3.0
|
|
450
|
+
- [x] Absolute latency floor (absolute_latency_floor_ms= param) -- v1.3.0
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Citation
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
Bhuiya, R. (2025). HuddleCluster: A Penguin-Inspired Self-Organizing Load Balancer
|
|
458
|
+
with Adaptive Thermal Eviction. https://github.com/rahadbhuiya/HuddleCluster
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## License
|
|
464
|
+
|
|
465
|
+
MIT — see LICENSE.
|
|
466
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
huddle_cluster.py,sha256=Y44l1XHiqsgiruLFEkSsmzKv7cIo80Fw85TzGGvepX8,54323
|
|
2
|
+
huddle_cluster-1.3.1.dist-info/WHEEL,sha256=zwUFtOH-KGODloEEaBQEGpGKFOXEzedEklcD4qr64mI,92
|
|
3
|
+
huddle_cluster-1.3.1.dist-info/METADATA,sha256=PqaRJFZJXcJca9RYxgC1ZmRH2qA9L4wIomYidSKXsnQ,16705
|
|
4
|
+
huddle_cluster-1.3.1.dist-info/LICENSE.txt,,
|
|
5
|
+
huddle_cluster-1.3.1.dist-info/RECORD,,
|