feat: add dual-host IM pressure test tool
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CombinedReport struct {
|
||||
RunID string `json:"run_id"`
|
||||
Scenario string `json:"scenario"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
Agents []AgentSummary `json:"agents"`
|
||||
ReadyConnections int `json:"ready_connections"`
|
||||
PublishAttempted int64 `json:"publish_attempted"`
|
||||
PublishAccepted int64 `json:"publish_accepted"`
|
||||
PublishRejected int64 `json:"publish_rejected"`
|
||||
ExpectedDeliveries int64 `json:"expected_deliveries"`
|
||||
ReceivedDeliveries int64 `json:"received_deliveries"`
|
||||
RealtimeLost int64 `json:"realtime_lost"`
|
||||
RealtimeLostStored int64 `json:"realtime_lost_but_persisted"`
|
||||
ArrivalRate float64 `json:"arrival_rate"`
|
||||
LossRate float64 `json:"loss_rate"`
|
||||
Duplicates int64 `json:"duplicates"`
|
||||
OutOfOrder int64 `json:"out_of_order"`
|
||||
Disconnects int64 `json:"disconnects"`
|
||||
HistoryChecked int64 `json:"history_checked"`
|
||||
HistoryFound int64 `json:"history_found"`
|
||||
PersistenceLost int64 `json:"persistence_lost"`
|
||||
Missing []MissingDelivery `json:"missing,omitempty"`
|
||||
Passed bool `json:"passed"`
|
||||
FailureReasons []string `json:"failure_reasons,omitempty"`
|
||||
}
|
||||
|
||||
func runMerge(aPath, bPath, output string) error {
|
||||
a, err := readAgentSummary(aPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := readAgentSummary(bPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.RunID != b.RunID || a.Scenario != b.Scenario {
|
||||
return fmt.Errorf("agent summaries do not belong to the same run")
|
||||
}
|
||||
report := CombinedReport{RunID: a.RunID, Scenario: a.Scenario, GeneratedAt: time.Now().UTC(), Agents: []AgentSummary{a, b}, Passed: true}
|
||||
for _, summary := range report.Agents {
|
||||
report.ReadyConnections += summary.ReadyConnections
|
||||
report.PublishAttempted += summary.PublishAttempted
|
||||
report.PublishAccepted += summary.PublishAccepted
|
||||
report.PublishRejected += summary.PublishRejected
|
||||
report.ExpectedDeliveries += summary.ExpectedPeer + summary.ExpectedGroup
|
||||
report.ReceivedDeliveries += summary.ReceivedPeer + summary.ReceivedGroup
|
||||
report.RealtimeLost += summary.RealtimeLost
|
||||
report.RealtimeLostStored += summary.RealtimeLostStored
|
||||
report.Duplicates += summary.Duplicates
|
||||
report.OutOfOrder += summary.OutOfOrder
|
||||
report.Disconnects += summary.Disconnects
|
||||
report.HistoryChecked += summary.HistoryChecked
|
||||
report.HistoryFound += summary.HistoryFound
|
||||
report.PersistenceLost += summary.PersistenceLost
|
||||
report.Missing = append(report.Missing, summary.Missing...)
|
||||
if len(summary.Errors) > 0 {
|
||||
report.FailureReasons = append(report.FailureReasons, fmt.Sprintf("agent %s errors=%d", summary.Machine, len(summary.Errors)))
|
||||
}
|
||||
}
|
||||
if report.ExpectedDeliveries > 0 {
|
||||
report.ArrivalRate = float64(report.ReceivedDeliveries) / float64(report.ExpectedDeliveries)
|
||||
report.LossRate = float64(report.RealtimeLost) / float64(report.ExpectedDeliveries)
|
||||
}
|
||||
if report.ReadyConnections != 2000 {
|
||||
report.FailureReasons = append(report.FailureReasons, fmt.Sprintf("ready connections=%d expected=2000", report.ReadyConnections))
|
||||
}
|
||||
if report.PublishRejected > 0 || report.RealtimeLost > 0 || report.PersistenceLost > 0 || report.Duplicates > 0 || report.OutOfOrder > 0 || report.Disconnects > 0 {
|
||||
report.FailureReasons = append(report.FailureReasons, "message correctness gate failed")
|
||||
}
|
||||
report.Passed = len(report.FailureReasons) == 0
|
||||
sort.Slice(report.Missing, func(i, j int) bool {
|
||||
if report.Missing[i].ClientMid == report.Missing[j].ClientMid {
|
||||
return report.Missing[i].ReceiverIndex < report.Missing[j].ReceiverIndex
|
||||
}
|
||||
return report.Missing[i].ClientMid < report.Missing[j].ClientMid
|
||||
})
|
||||
return writeJSONFile(output, report)
|
||||
}
|
||||
|
||||
func readAgentSummary(path string) (AgentSummary, error) {
|
||||
var summary AgentSummary
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return summary, err
|
||||
}
|
||||
if err = json.Unmarshal(raw, &summary); err != nil {
|
||||
return summary, err
|
||||
}
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func writeJSONFile(path string, value any) error {
|
||||
raw, err := json.MarshalIndent(value, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dir := filepath.Dir(path); dir != "." {
|
||||
if err = os.MkdirAll(dir, 0o750); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.WriteFile(path, append(raw, '\n'), 0o600)
|
||||
}
|
||||
Reference in New Issue
Block a user