fix(group): bound push pressure and synchronize load agents

This commit is contained in:
richjohnrambo
2026-10-04 00:49:27 +08:00
parent 746f75ab0f
commit 7b25365ec3
14 changed files with 476 additions and 47 deletions
+50
View File
@@ -15,6 +15,7 @@ func TestCoordinatorResetsBarrierWhenConnectionCountDrops(t *testing.T) {
start: StartInfo{RunID: "run-reset", Scenario: "group"}, startDelay: time.Minute,
commitDelay: time.Second, expectedAgents: 2, ready: make(map[string]AgentReady),
armed: make(map[string]bool), ledgers: make(map[string][]AcceptedMessage),
started: make(map[string]time.Time),
summaries: make(map[string]AgentSummary), complete: make(chan struct{}),
}
postReady := func(ready AgentReady) StartInfo {
@@ -52,6 +53,7 @@ func TestCoordinatorClientWaitsForBothAgentsAndMergesLedger(t *testing.T) {
commitDelay: 50 * time.Millisecond, expectedAgents: 2,
ready: make(map[string]AgentReady), ledgers: make(map[string][]AcceptedMessage),
armed: make(map[string]bool), summaries: make(map[string]AgentSummary), complete: make(chan struct{}),
started: make(map[string]time.Time),
}
server := httptest.NewServer(testCoordinatorMux(state))
defer server.Close()
@@ -116,6 +118,7 @@ func testCoordinatorMux(state *coordinatorState) *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/v1/ready", state.handleReady)
mux.HandleFunc("/v1/start", state.handleStart)
mux.HandleFunc("/v1/started", state.handleStarted)
mux.HandleFunc("/v1/ledger", state.handleLedger)
mux.HandleFunc("/v1/observed", state.handleObserved)
mux.HandleFunc("/v1/history", state.handleHistory)
@@ -123,3 +126,50 @@ func testCoordinatorMux(state *coordinatorState) *http.ServeMux {
mux.HandleFunc("/v1/status", state.handleStatus)
return mux
}
func TestCoordinatorRejectsLateAgentForBothMachines(t *testing.T) {
startAt := time.Now().Add(-maxAgentStartSkew - time.Second).UTC()
state := &coordinatorState{
start: StartInfo{RunID: "run-late", Scenario: "group", StartAt: startAt, Committed: true},
expectedAgents: 2, started: make(map[string]time.Time),
}
server := httptest.NewServer(testCoordinatorMux(state))
defer server.Close()
client := newCoordinatorClient(server.URL)
if err := client.started(context.Background(), "b", startAt); err == nil {
t.Fatal("late agent must be rejected")
}
if err := client.started(context.Background(), "a", startAt); err == nil {
t.Fatal("the other agent must observe the same rejected run")
}
}
func TestCoordinatorReleasesAgentsOnlyAfterBothReachStart(t *testing.T) {
startAt := time.Now().UTC()
state := &coordinatorState{
start: StartInfo{RunID: "run-synced", Scenario: "group", StartAt: startAt, Committed: true},
expectedAgents: 2, started: make(map[string]time.Time),
}
server := httptest.NewServer(testCoordinatorMux(state))
defer server.Close()
client := newCoordinatorClient(server.URL)
aDone := make(chan error, 1)
go func() { aDone <- client.started(context.Background(), "a", startAt) }()
time.Sleep(50 * time.Millisecond)
select {
case err := <-aDone:
t.Fatalf("machine A passed before B arrived: %v", err)
default:
}
if err := client.started(context.Background(), "b", startAt); err != nil {
t.Fatalf("machine B start: %v", err)
}
select {
case err := <-aDone:
if err != nil {
t.Fatalf("machine A start: %v", err)
}
case <-time.After(time.Second):
t.Fatal("machine A was not released")
}
}