fix: harden single-machine load handling
This commit is contained in:
@@ -458,8 +458,18 @@ func newHistoryQuery(client *WSClient, topic string, messages []AcceptedMessage,
|
||||
for _, message := range messages {
|
||||
expected = append(expected, message.ClientMid)
|
||||
}
|
||||
return historyQuery{client: client, topic: topic, since: messages[0].Seq,
|
||||
before: messages[len(messages)-1].Seq + 1, limit: limit, expected: expected}
|
||||
since := messages[0].Seq
|
||||
before := messages[len(messages)-1].Seq + 1
|
||||
// P2P topics interleave both senders. The requested range must leave room
|
||||
// for non-target messages between the expected sequence numbers.
|
||||
if span := before - since; span > int64(limit) {
|
||||
limit = int(span)
|
||||
}
|
||||
if limit > 200 {
|
||||
limit = 200
|
||||
}
|
||||
return historyQuery{client: client, topic: topic, since: since,
|
||||
before: before, limit: limit, expected: expected}
|
||||
}
|
||||
|
||||
func (r *agentRun) waitHistoryPage(ctx context.Context, expected []string, timeout time.Duration) {
|
||||
@@ -659,13 +669,24 @@ func (r *agentRun) execute(ctx context.Context, start time.Time, groupTopic stri
|
||||
case "p2p":
|
||||
return r.scheduleP2P(ctx, start, r.cfg.UsersPerMachine, r.cfg.P2PMessages, mustDuration(r.cfg.P2PInterval))
|
||||
case "group":
|
||||
return r.scheduleGroup(ctx, start, groupTopic, 1, 500, 2)
|
||||
return r.scheduleGroup(ctx, start, groupTopic, 1, scaledUsers(r.cfg.UsersPerMachine, 1, 2), 2)
|
||||
case "mixed":
|
||||
p2pUsers := scaledUsers(r.cfg.UsersPerMachine, 3, 10)
|
||||
groupSenders := scaledUsers(r.cfg.UsersPerMachine, 1, 4)
|
||||
if p2pUsers+groupSenders > r.cfg.UsersPerMachine {
|
||||
groupSenders = r.cfg.UsersPerMachine - p2pUsers
|
||||
}
|
||||
if groupSenders <= 0 {
|
||||
return fmt.Errorf("users_per_machine=%d is too small for mixed scenario", r.cfg.UsersPerMachine)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
errCh := make(chan error, 2)
|
||||
wg.Add(2)
|
||||
go func() { defer wg.Done(); errCh <- r.scheduleP2P(ctx, start, 300, 5, 20*time.Second) }()
|
||||
go func() { defer wg.Done(); errCh <- r.scheduleGroup(ctx, start, groupTopic, 301, 550, 2) }()
|
||||
go func() { defer wg.Done(); errCh <- r.scheduleP2P(ctx, start, p2pUsers, 5, 20*time.Second) }()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
errCh <- r.scheduleGroup(ctx, start, groupTopic, p2pUsers+1, p2pUsers+groupSenders, 2)
|
||||
}()
|
||||
wg.Wait()
|
||||
close(errCh)
|
||||
for err := range errCh {
|
||||
@@ -679,6 +700,17 @@ func (r *agentRun) execute(ctx context.Context, start time.Time, groupTopic stri
|
||||
}
|
||||
}
|
||||
|
||||
func scaledUsers(total, numerator, denominator int) int {
|
||||
count := total * numerator / denominator
|
||||
if count < 1 {
|
||||
return 1
|
||||
}
|
||||
if count > total {
|
||||
return total
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (r *agentRun) scheduleP2P(ctx context.Context, start time.Time, users, messages int, interval time.Duration) error {
|
||||
var wg sync.WaitGroup
|
||||
for index := 1; index <= users; index++ {
|
||||
@@ -858,9 +890,10 @@ func (r *agentRun) drainDuration() (time.Duration, error) {
|
||||
|
||||
func (r *agentRun) close() {
|
||||
clients := r.clientsSnapshot()
|
||||
var interval time.Duration
|
||||
if r.cfg != nil && r.cfg.CloseRamp != "" && len(clients) > 1 {
|
||||
interval = mustDuration(r.cfg.CloseRamp) / time.Duration(len(clients)-1)
|
||||
sort.Slice(clients, func(i, j int) bool { return clients[i].account.Index < clients[j].account.Index })
|
||||
interval, initialDelay := closeSchedule(r.cfg, r.machine, len(clients))
|
||||
if initialDelay > 0 {
|
||||
time.Sleep(initialDelay)
|
||||
}
|
||||
for index, client := range clients {
|
||||
client.Close()
|
||||
@@ -870,6 +903,19 @@ func (r *agentRun) close() {
|
||||
}
|
||||
}
|
||||
|
||||
func closeSchedule(cfg *Config, machine string, clients int) (interval, initialDelay time.Duration) {
|
||||
if cfg == nil || cfg.CloseRamp == "" || clients <= 1 {
|
||||
return 0, 0
|
||||
}
|
||||
// The two logical agents share one global ramp. Interleaving their slots avoids
|
||||
// doubling the disconnect rate when both agents begin cleanup at the same time.
|
||||
interval = 2 * mustDuration(cfg.CloseRamp) / time.Duration(clients-1)
|
||||
if machine == "b" {
|
||||
initialDelay = interval / 2
|
||||
}
|
||||
return interval, initialDelay
|
||||
}
|
||||
|
||||
func waitUntil(ctx context.Context, target time.Time) error {
|
||||
delay := time.Until(target)
|
||||
if delay <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user