Standbys that answer the client API themselves, behind router.active_active
CI / test (push) Successful in 9m35s
CI / test (push) Successful in 9m35s
A standby follows the leader's Ready replicas over the agent port, every two seconds, and reports back the replicas its router used and the on-demand models it was asked for. No engine keys travel: every control plane derives them from the cluster secret. Budgets are raised to the store's every 30 s. Unheard from the leader for a minute, a standby passes /v1 on as before. Off by default. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f664592948
commit
8b7eefef16
22 files changed
+936
-15
No files matched your search
@@ -182,6 +182,7 @@ func (a *API) Register(mux Mux) {
|
||||
a.registerPresets(mux)
|
||||
mux.HandleFunc("GET /api/v1/models", a.listModels)
|
||||
mux.HandleFunc("GET /api/v1/replicas", a.listReplicas)
|
||||
a.registerRouting(mux)
|
||||
// Listing is a read and deleting is a separate verb, so a mistyped
|
||||
// command cannot remove anything.
|
||||
mux.HandleFunc("GET /api/v1/weights/orphans", a.listOrphans)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
llmgw "gitea.mynodes.xyz/ilker/llmgw"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/bench"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/bench/eval"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/control"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/control/fabrics"
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/control/federation"
|
||||
@@ -194,6 +195,15 @@ var Routes = []Route{
|
||||
{Method: "GET", Path: "/api/v1/replicas", Summary: "Every replica, with its shards",
|
||||
Query: []Param{{Name: "model", Description: "Only this model's replicas."}},
|
||||
Response: []ReplicaView{}},
|
||||
{Method: "GET", Path: "/api/v1/routing", Summary: "The Ready replicas a standby routes to",
|
||||
Description: "Read by a standby control plane when router.active_active is on: each replica's address, " +
|
||||
"model, profile, pool and weight, without its key, and which catalog is in force.",
|
||||
Response: control.Routing{}},
|
||||
{Method: "POST", Path: "/api/v1/routing/activity", Summary: "A standby's router reports what it used",
|
||||
Description: "When it last sent each replica a request, and the on-demand models it was asked for, " +
|
||||
"so the leader does not evict a replica busy behind a standby. Answered with the routing, " +
|
||||
"so one request a tick does both.",
|
||||
Request: control.Activity{}, Response: control.Routing{}},
|
||||
{Method: "GET", Path: "/api/v1/explain-placement", Summary: "Why a model could not be placed",
|
||||
Query: []Param{{Name: "model", Required: true, Description: "The model's name or an alias."}},
|
||||
Response: ExplainView{}},
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package adminapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/control"
|
||||
)
|
||||
|
||||
// registerRouting is what a standby answering requests itself reads from the
|
||||
// leader, and reports back to it. Reached by the standby through the agent
|
||||
// port with its control-plane certificate, like the rest of the forwarded API.
|
||||
func (a *API) registerRouting(mux Mux) {
|
||||
mux.HandleFunc("GET /api/v1/routing", func(w http.ResponseWriter, _ *http.Request) {
|
||||
writeJSON(w, http.StatusOK, a.ctl.Routing())
|
||||
})
|
||||
mux.HandleFunc("POST /api/v1/routing/activity", func(w http.ResponseWriter, r *http.Request) {
|
||||
var act control.Activity
|
||||
if err := json.NewDecoder(io.LimitReader(r.Body, 8<<20)).Decode(&act); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
a.ctl.RecordActivity(act)
|
||||
writeJSON(w, http.StatusOK, a.ctl.Routing())
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user