Every model of a stopped system asks for no replica and so has what it asks for. Until the control plane took the setting up, seconds later, start --wait saw 18 of 18 satisfied and returned at once, with nothing started. It now counts only once the control plane says it is in the state asked for, both ways. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2d14918b99
commit
63f7c8cfd7
2 files changed
+43
-2
No files matched your search
@@ -11,6 +11,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -102,3 +103,35 @@ func TestStopSetsTheClustersServingStopped(t *testing.T) {
|
||||
t.Errorf("%s", out.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Started while the control plane has not yet taken the setting up: every
|
||||
// model of a stopped system has the nothing it asks for, which is not
|
||||
// serving.
|
||||
func TestStartWaitsForTheControlPlaneToServe(t *testing.T) {
|
||||
old := servicePoll
|
||||
servicePoll = time.Millisecond
|
||||
t.Cleanup(func() { servicePoll = old })
|
||||
answers := []adminapi.Summary{
|
||||
{Stopped: true, Models: adminapi.ModelTotals{Total: 2, Satisfied: 2}},
|
||||
{Models: adminapi.ModelTotals{Total: 2, Satisfied: 0}},
|
||||
{Models: adminapi.ModelTotals{Total: 2, Satisfied: 2}},
|
||||
}
|
||||
asked := 0
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(answers[min(asked, len(answers)-1)])
|
||||
asked++
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetContext(context.Background())
|
||||
c := &ctlClient{base: srv.URL, http: srv.Client()}
|
||||
if err := waitForService(cmd, func() *ctlClient { return c }, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if asked != 3 {
|
||||
t.Errorf("returned after %d answers: %s", asked, out.String())
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -190,13 +190,21 @@ func newSystemCmds() []*cobra.Command {
|
||||
return []*cobra.Command{status, switchService(true), switchService(false), restart}
|
||||
}
|
||||
|
||||
// servicePoll is how often --wait asks; a variable so tests need not wait.
|
||||
var servicePoll = 5 * time.Second
|
||||
|
||||
// waitForService waits until no replica runs (stopped) or every model has
|
||||
// the replicas it asks for (started).
|
||||
//
|
||||
// Only once the control plane says it is in the state asked for: it takes the
|
||||
// setting up at its next look at the store, seconds later, and until then a
|
||||
// stopped system's models, which ask for nothing, all have what they ask for —
|
||||
// so llmgw start --wait returned at once, with nothing started.
|
||||
func waitForService(cmd *cobra.Command, call func() *ctlClient, stop bool) error {
|
||||
deadline := time.Now().Add(15 * time.Minute)
|
||||
for time.Now().Before(deadline) {
|
||||
var s adminapi.Summary
|
||||
if err := call().get(cmd.Context(), "/api/v1/summary", &s); err == nil {
|
||||
if err := call().get(cmd.Context(), "/api/v1/summary", &s); err == nil && s.Stopped == stop {
|
||||
if stop && s.Replicas.Ready+s.Replicas.Starting+s.Replicas.Draining == 0 {
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "stopped: no replica runs")
|
||||
return nil
|
||||
@@ -210,7 +218,7 @@ func waitForService(cmd *cobra.Command, call func() *ctlClient, stop bool) error
|
||||
select {
|
||||
case <-cmd.Context().Done():
|
||||
return cmd.Context().Err()
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-time.After(servicePoll):
|
||||
}
|
||||
}
|
||||
return errors.New("not there within fifteen minutes; llmgw status says where it stands")
|
||||
|
||||
Reference in new issue
Block a user