A node given a role takes only what asks for it
The judges' machine was selected by the judge's profile, but any other model could still be placed beside it. A node labelled llmgw.role now takes only profiles whose selector asks for that role. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
51ca09287f
commit
34764f5ef0
4 files changed
+31
-2
No files matched your search
+2
-1
@@ -32,7 +32,8 @@ APIs change between minors without a deprecation period.
|
||||
the store's two; `server init` takes `--judge-node user@host` for each. Each is added as a node labelled
|
||||
`llmgw.role: judge`, and the judge's profile selects it, so the judges run there alone. Under load on a 4-core
|
||||
control plane that was also a node, 38 of 60 requests were refused for want of a judgment. With the judges on a node
|
||||
of their own, 2 were.
|
||||
of their own, 2 were. A node with an `llmgw.role` label takes only profiles whose node selector asks for that role,
|
||||
so no other model is placed on the judges' machine.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/control/placement"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/onboard"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/settings"
|
||||
)
|
||||
@@ -18,7 +19,7 @@ import (
|
||||
|
||||
// judgeRoleLabel marks a node as the judges'.
|
||||
const (
|
||||
judgeRoleLabel = "llmgw.role"
|
||||
judgeRoleLabel = placement.RoleLabel
|
||||
judgeRoleValue = "judge"
|
||||
)
|
||||
|
||||
|
||||
@@ -548,6 +548,10 @@ func hasWeights(source string, n inventory.Node) (present, known bool) {
|
||||
// It returns the label rather than a bare false so the rejection can name it:
|
||||
// "does not carry ram=32g" is a placement failure someone can act on, and
|
||||
// "no candidates were considered" is not.
|
||||
// RoleLabel gives a node a role, such as judge: it then takes only profiles
|
||||
// whose node selector asks for that role.
|
||||
const RoleLabel = "llmgw.role"
|
||||
|
||||
func unmatchedLabel(selector, labels map[string]string) (string, bool) {
|
||||
for k, want := range selector {
|
||||
if got, ok := labels[k]; !ok || got != want {
|
||||
@@ -602,6 +606,13 @@ func (s *Scheduler) eligibleNodes(p Profile, fabric string, avoid []string, reje
|
||||
reject("does not carry " + miss)
|
||||
continue
|
||||
}
|
||||
// A node given a role is kept for what asks for it: the judges'
|
||||
// machine runs the judges alone, so nothing beside them takes the
|
||||
// cores a judgment waits on.
|
||||
if role, ok := n.Labels[RoleLabel]; ok && p.NodeSelector[RoleLabel] != role {
|
||||
reject("kept for " + RoleLabel + "=" + role)
|
||||
continue
|
||||
}
|
||||
// The engine has to be installed here. The field has been on this
|
||||
// struct since placement was written and was never read, so a node
|
||||
// with no vLLM was as good a candidate as one with it: the replica was
|
||||
|
||||
@@ -1099,3 +1099,19 @@ func TestAProfileCanAskForAnEngineVersion(t *testing.T) {
|
||||
t.Errorf("the rejection should say what the node has:\n%s", res.Explain())
|
||||
}
|
||||
}
|
||||
|
||||
func TestANodeGivenARoleTakesOnlyWhatAsksForIt(t *testing.T) {
|
||||
judge := node("judge", "", 0)
|
||||
judge.Labels = map[string]string{RoleLabel: "judge"}
|
||||
s := New(store(judge))
|
||||
// A model that does not ask for the judges' machine is not put there.
|
||||
res := s.Place([]Profile{{Name: "cpu", Engine: "llamacpp"}}, nil)
|
||||
if res.Placement != nil || !strings.Contains(res.Explain(), "kept for llmgw.role=judge") {
|
||||
t.Fatalf("an ordinary model went on the judges' machine:\n%s", res.Explain())
|
||||
}
|
||||
// The judge, which asks for it, is.
|
||||
res = s.Place([]Profile{{Name: "cpu", Engine: "llamacpp", NodeSelector: map[string]string{RoleLabel: "judge"}}}, nil)
|
||||
if res.Placement == nil || res.Placement.Nodes[0].NodeID != "judge" {
|
||||
t.Fatalf("the judge was not placed on its machine:\n%s", res.Explain())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user