feat: production-grade routing cache + stale detection fixes

Three-tier routing cache (ESL global -> file -> HTTP) eliminates HTTP
from call path for 2500-5000 concurrent calls. Lua reads cached route
in ~0.1us instead of blocking on api:execute('curl', ...).

Engine fixes:
- recalcBestLocked now skips stale nodes (was 5 min bug -> now ~10-15s)
- PickNodeForCall action_type='node' checks staleness for consistency
- onMetric callback pushes cache on every metric (no ticker delay)

Config:
- stale_threshold_sec default 20 -> 10 (industry standard)
- contrib/ included in Makefile deploy target
- route.lua paths updated for /opt/pulse-lets-go
This commit is contained in:
Maksim Totmin
2026-06-25 22:23:16 +07:00
parent a7722af29e
commit 78785e54e1
11 changed files with 266 additions and 86 deletions
+6
View File
@@ -456,12 +456,18 @@ func (e *Engine) scoreNodeLocked(ns *models.NodeState, firstMetric bool) {
// recalcBestLocked пересчитывает кэш лучшей ноды.
// Вызывающий держит write lock.
// Проверяет staleness (now - ns.TS > staleThreshold) — исключает ноды без свежих метрик.
func (e *Engine) recalcBestLocked() {
bestID := ""
bestScore := -999.0
anyHealthy := false
now := time.Now().Unix()
staleThreshold := int64(e.cfg.StaleThresholdSec)
for id, ns := range e.nodes {
if now-ns.TS > staleThreshold {
continue
}
if ns.Score >= 0 {
anyHealthy = true
}
+3 -1
View File
@@ -318,8 +318,10 @@ func (e *Engine) PickNodeForCall(callerID, dest, ingress string) (nodeID string,
case "node":
e.mu.RLock()
ns, ok := e.nodes[cr.ActionNodeID]
now := time.Now().Unix()
staleThreshold := int64(e.cfg.StaleThresholdSec)
e.mu.RUnlock()
if ok && ns.Score >= 0 {
if ok && ns.Score >= 0 && now-ns.TS <= staleThreshold {
nodeID, score, matchedRule = cr.ActionNodeID, 100, cr.Name
resolved = true
}