SysDeck/web/mini-services/fester/scheduler.ts

45 lines
1.4 KiB
TypeScript

/**
* Fester weighted scheduler — port of backend/scheduler/optimizer.py.
* score = (100 - cpu_load) - temp + policy bonus - instability*10
*/
import type { NodeState } from './nodes'
export interface ScheduleDecision {
node: string
score: number
reason: string
scores: { node: string; score: number }[]
}
export function chooseBestNode(states: NodeState[], policy?: Map<string, 'preferred' | 'avoid'>): ScheduleDecision {
let best: NodeState | null = null
let bestScore = -Infinity
const scores: { node: string; score: number }[] = []
for (const s of states) {
if (s.state === 'offline') continue
let score = 100 - s.cpu_load - s.temp
const pol = policy?.get(s.name)
if (pol === 'preferred') score += 20
if (pol === 'avoid') score -= 50
score -= s.instability * 10
// prefer nodes with free job slots
score -= (s.active_jobs / Math.max(1, s.max_jobs)) * 30
s.score = Math.round(score * 10) / 10
scores.push({ node: s.name, score: s.score })
if (score > bestScore) {
bestScore = score
best = s
}
}
const chosen = best ?? states[0]
return {
node: chosen.name,
score: Math.round((chosen.score ?? bestScore) * 10) / 10,
reason: `cpu ${chosen.cpu_load.toFixed(0)}% · temp ${chosen.temp.toFixed(0)}°C · jobs ${chosen.active_jobs}/${chosen.max_jobs}`,
scores: scores.sort((a, b) => b.score - a.score),
}
}