pulse-lets-go/docs/scoring.md

2.1 KiB
Raw Permalink Blame History

Scoring Engine

Вычисляет score (0..100) для каждой ноды. Ноды со score = -100 исключаются из роутинга.

Lethal Conditions (score → -100)

Если хотя бы одно условие истинно — нода исключается: If at least one is true — node is excluded from routing.

Condition Threshold
status != "ok" Any status other than ok
Stale > stale_threshold_sec (default 20s)
active_calls >= max_calls 100% capacity used
idle_cpu < idle_cpu_min < 5%
call_failure_rate > call_failure_rate_lethal > 15%
disabled == true Manual admin disable

Weighted Score (0..100)

call_score = clamp(100 - (active_calls / max_calls * 100), 0, 100)
load_score = clamp(100 - (load_avg * 50), 0, 100)
idle_score = clamp(idle_cpu, 0, 100)
fail_score = clamp(100 - call_failure_rate, 0, 100)

score = call_score × 0.40 +
        load_score × 0.30 +
        idle_score × 0.20 +
        fail_score × 0.10

Weight defaults (configurable in config.json): call 40%, load 30%, idle 20%, fail 10%.

Example

Node pbx-01: active_calls=42, max_calls=250, load_avg=0.85, idle_cpu=35, fail_rate=0.5

call_score = clamp(100 - (42/250)*100, 0, 100) = 83.2
load_score = clamp(100 - 0.85*50, 0, 100)      = 57.5
idle_score = clamp(35, 0, 100)                   = 35.0
fail_score = clamp(100 - 0.5, 0, 100)            = 99.5

score = 83.2×0.40 + 57.5×0.30 + 35.0×0.20 + 99.5×0.10 = 70.1

Caching

best_node_id, best_score, and fallback_active are recalculated on every metric update. /api/route reads them under RLock — O(1) constant time.

Выбор ноды из кэша зависит от режима балансировки.

Configuration

{
  "stale_threshold_sec": 20,
  "scoring": {
    "idle_cpu_min": 5,
    "call_failure_rate_lethal": 15.0,
    "weights": {
      "call_score": 0.40,
      "load_score": 0.30,
      "idle_score": 0.20,
      "fail_score": 0.10
    }
  }
}

Weights must sum to 1.0. All thresholds configurable in config.json.