feat:新增自动化拉取并汇总分析数据功能

This commit is contained in:
nnbcccscdscdsc
2026-03-24 16:05:14 +08:00
parent 9503862eda
commit f0d297f272
5 changed files with 411 additions and 1 deletions

View File

@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"reflect"
"sort"
"testing"
"omnisocketgo/cmd/internal/protocol"
@@ -188,6 +189,147 @@ func TestLoadAndWriteSummaryFiles(t *testing.T) {
}
}
func TestLoadEventsFromFilesWithSharedMaxOffsetFiltersToSharedCutoff(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
firstMessageIDs []uint64
secondMessageIDs []uint64
offset uint64
wantCutoff *uint64
wantMessageIDs []uint64
}{
{
name: "same max message id rolls back one",
firstMessageIDs: []uint64{1, 2, 3, 4, 5, 6, 7},
secondMessageIDs: []uint64{1, 2, 3, 4, 5, 6, 7},
offset: 1,
wantCutoff: uint64Ptr(6),
wantMessageIDs: []uint64{1, 2, 3, 4, 5, 6},
},
{
name: "smaller input max wins before rollback",
firstMessageIDs: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9},
secondMessageIDs: []uint64{1, 2, 3, 4, 5, 6, 7},
offset: 1,
wantCutoff: uint64Ptr(6),
wantMessageIDs: []uint64{1, 2, 3, 4, 5, 6},
},
{
name: "not enough shared messages yields empty result",
firstMessageIDs: []uint64{1},
secondMessageIDs: []uint64{1},
offset: 1,
wantCutoff: uint64Ptr(0),
wantMessageIDs: nil,
},
}
for _, tt := range testCases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
firstPath := filepath.Join(tempDir, "first.jsonl")
secondPath := filepath.Join(tempDir, "second.jsonl")
writeEventsJSONL(t, firstPath, testEventsForMessageIDs(tt.firstMessageIDs, "peer-a", "peer-b"))
writeEventsJSONL(t, secondPath, testEventsForMessageIDs(tt.secondMessageIDs, "peer-b", "peer-a"))
events, cutoff, err := LoadEventsFromFilesWithSharedMaxOffset([]string{firstPath, secondPath}, tt.offset)
if err != nil {
t.Fatalf("LoadEventsFromFilesWithSharedMaxOffset() error = %v", err)
}
if !reflect.DeepEqual(cutoff, tt.wantCutoff) {
t.Fatalf("cutoff = %v, want %v", cutoff, tt.wantCutoff)
}
if got := businessMessageIDs(events); !reflect.DeepEqual(got, tt.wantMessageIDs) {
t.Fatalf("message IDs = %v, want %v", got, tt.wantMessageIDs)
}
})
}
}
func TestLoadEventsFromFilesWithSharedMaxOffsetPreservesEarlierSummaries(t *testing.T) {
tempDir := t.TempDir()
firstPath := filepath.Join(tempDir, "first.jsonl")
secondPath := filepath.Join(tempDir, "second.jsonl")
writeEventsJSONL(t, firstPath, []Event{
{TsUnixNano: 100, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-a", To: "peer-b", BodySize: 320},
{TsUnixNano: 120, Event: EventATXSched, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-a", To: "peer-b", BodySize: 320},
{TsUnixNano: 140, Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-a", To: "peer-b", BodySize: 320},
{TsUnixNano: 180, Event: EventBRXSoftware, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-a", To: "peer-b", BodySize: 320},
{TsUnixNano: 220, Event: EventBAppRecv, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-a", To: "peer-b", BodySize: 320},
{TsUnixNano: 260, Event: EventBPersistEnd, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-a", To: "peer-b", BodySize: 320},
{TsUnixNano: 300, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-a", To: "peer-b", BodySize: 160},
{TsUnixNano: 330, Event: EventATXSched, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-a", To: "peer-b", BodySize: 160},
{TsUnixNano: 360, Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-a", To: "peer-b", BodySize: 160},
{TsUnixNano: 390, Event: EventBRXSoftware, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-a", To: "peer-b", BodySize: 160},
{TsUnixNano: 420, Event: EventBAppRecv, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-a", To: "peer-b", BodySize: 160},
{TsUnixNano: 470, Event: EventBPersistEnd, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-a", To: "peer-b", BodySize: 160},
{TsUnixNano: 500, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-a", To: "peer-b", BodySize: 80},
{TsUnixNano: 520, Event: EventATXSched, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-a", To: "peer-b", BodySize: 80},
{TsUnixNano: 540, Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-a", To: "peer-b", BodySize: 80},
{TsUnixNano: 560, Event: EventBRXSoftware, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-a", To: "peer-b", BodySize: 80},
{TsUnixNano: 580, Event: EventBAppRecv, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-a", To: "peer-b", BodySize: 80},
{TsUnixNano: 600, Event: EventBPersistEnd, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-a", To: "peer-b", BodySize: 80},
{TsUnixNano: 700, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 4, From: "peer-a", To: "peer-b", BodySize: 40},
})
writeEventsJSONL(t, secondPath, []Event{
{TsUnixNano: 90, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 95, Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 150, Event: EventBAppRecv, MessageType: protocol.MessageTypeText, MessageID: 1, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 290, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 295, Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 350, Event: EventBAppRecv, MessageType: protocol.MessageTypeText, MessageID: 2, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 490, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 495, Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 550, Event: EventBAppRecv, MessageType: protocol.MessageTypeText, MessageID: 3, From: "peer-b", To: "peer-a", BodySize: 20},
{TsUnixNano: 690, Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: 4, From: "peer-b", To: "peer-a", BodySize: 20},
})
events, cutoff, err := LoadEventsFromFilesWithSharedMaxOffset([]string{firstPath, secondPath}, 1)
if err != nil {
t.Fatalf("LoadEventsFromFilesWithSharedMaxOffset() error = %v", err)
}
if !reflect.DeepEqual(cutoff, uint64Ptr(3)) {
t.Fatalf("cutoff = %v, want %v", cutoff, uint64Ptr(3))
}
summaries := SummarizeEvents(events)
if got := len(summaries); got != 6 {
t.Fatalf("summary count = %d, want 6", got)
}
for _, summary := range summaries {
if summary.MessageID == 4 {
t.Fatalf("message 4 should have been truncated from summaries: %+v", summary)
}
}
var forwardMessageTwo Summary
found := false
for _, summary := range summaries {
if summary.From == "peer-a" && summary.To == "peer-b" && summary.MessageID == 2 {
forwardMessageTwo = summary
found = true
break
}
}
if !found {
t.Fatal("summary for message 2 peer-a -> peer-b not found")
}
if got := ptrValue(forwardMessageTwo.EndToEndLatencyNS); got != 170 {
t.Fatalf("message 2 EndToEndLatencyNS = %d, want 170", got)
}
if got := ptrValue(forwardMessageTwo.ApproxRTTNS); got != 190 {
t.Fatalf("message 2 ApproxRTTNS = %d, want 190", got)
}
}
func ptrValue(value *int64) int64 {
if value == nil {
return 0
@@ -201,3 +343,57 @@ func ptrValueFloat(value *float64) float64 {
}
return *value
}
func uint64Ptr(value uint64) *uint64 {
return &value
}
func businessMessageIDs(events []Event) []uint64 {
seen := make(map[uint64]struct{})
var ids []uint64
for _, event := range events {
if !IsBusinessEvent(event) {
continue
}
if _, ok := seen[event.MessageID]; ok {
continue
}
seen[event.MessageID] = struct{}{}
ids = append(ids, event.MessageID)
}
sort.Slice(ids, func(i, j int) bool {
return ids[i] < ids[j]
})
return ids
}
func testEventsForMessageIDs(messageIDs []uint64, from, to string) []Event {
events := make([]Event, 0, len(messageIDs)*2)
for _, messageID := range messageIDs {
events = append(events,
Event{TsUnixNano: int64(messageID*100 + 10), Event: EventAAppPrepBegin, MessageType: protocol.MessageTypeText, MessageID: messageID, From: from, To: to, BodySize: 32},
Event{TsUnixNano: int64(messageID*100 + 20), Event: EventATXSoftware, MessageType: protocol.MessageTypeText, MessageID: messageID, From: from, To: to, BodySize: 32},
)
}
return events
}
func writeEventsJSONL(t *testing.T, path string, events []Event) {
t.Helper()
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
if err != nil {
t.Fatalf("os.OpenFile(%s) error = %v", path, err)
}
defer file.Close()
encoder := json.NewEncoder(file)
for _, event := range events {
if err := encoder.Encode(event); err != nil {
t.Fatalf("encoder.Encode(%s) error = %v", path, err)
}
}
}