Skip to content

WIP: test if watch is sequential #18264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/robustness/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ func ToWatchResponse(r clientv3.WatchResponse, baseTime time.Time) model.WatchRe
}
resp.IsProgressNotify = r.IsProgressNotify()
resp.Revision = r.Header.Revision
resp.MemberId = r.Header.MemberId
err := r.Err()
if err != nil {
resp.Error = r.Err().Error()
Expand Down
1 change: 1 addition & 0 deletions tests/robustness/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func testRobustness(ctx context.Context, t *testing.T, lg *zap.Logger, s testSce
watchProgressNotifyEnabled := c.Cfg.ServerConfig.ExperimentalWatchProgressNotifyInterval != 0
validateGotAtLeastOneProgressNotify(t, r.Client, s.watch.requestProgress || watchProgressNotifyEnabled)
}
validateWatchSequential(t, r.Client)
validateConfig := validate.Config{ExpectRevisionUnique: s.traffic.ExpectUniqueRevision()}
r.Visualize = validate.ValidateAndReturnVisualize(t, lg, validateConfig, r.Client, persistedRequests, 5*time.Minute)

Expand Down
1 change: 1 addition & 0 deletions tests/robustness/model/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ type WatchResponse struct {
IsProgressNotify bool
Revision int64
Time time.Duration
MemberId uint64
Error string
}
56 changes: 56 additions & 0 deletions tests/robustness/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package robustness

import (
"context"
"sort"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -141,3 +142,58 @@ external:
t.Errorf("Progress notify does not match, expect: %v, got: %v", expectProgressNotify, gotProgressNotify)
}
}

type timeLastRevision struct {
time time.Duration
lastRevision int64
}

func combineWatchResponses(reports []report.ClientReport) map[uint64][]timeLastRevision {
result := make(map[uint64][]timeLastRevision)
for _, r := range reports {
for _, op := range r.Watch {
for _, resp := range op.Responses {
if len(resp.Events) == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to double check, but I think we could consider recording progress notify here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the responses are aggregated here we can do.

continue
}
result[resp.MemberId] = append(result[resp.MemberId], timeLastRevision{time: resp.Time, lastRevision: resp.Events[len(resp.Events)-1].Revision})
}
}
}
for memberId, structs := range result {
sort.Slice(structs, func(i, j int) bool {
return structs[i].time < structs[j].time
})
result[memberId] = structs
}
return result
}

func validateWatchSequential(t *testing.T, reports []report.ClientReport) {
combinedWatchResponses := combineWatchResponses(reports)
for _, r := range reports {
for _, op := range r.Watch {
if op.Request.Revision != 0 {
continue
}
for _, resp := range op.Responses {
Copy link
Member

@serathius serathius Aug 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(resp.Events) == 0 {
continue
}

if resp.Events[0].Revision < lastSeenRevision {
t.Errorf("Watch has gone back")
}
lastSeenRevision = resp.Events[len(resp.Events)-1].Revision

if len(resp.Events) == 0 {
continue
}
var lastMemberWatchRevision int64
for i, c := range combinedWatchResponses[resp.MemberId] {
// Reports are sorted by time, find first greater or equal and use previous one.
if resp.Time >= c.time {
if i == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why skip the first one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for review, i'm using an logic here which is find the first equal or greater and use previous one. And there is nothing before 0.
And if i remember correctly we aggregate all responses here, and there is nothing before the first response.

continue
}
lastMemberWatchRevision = combinedWatchResponses[resp.MemberId][i-1].lastRevision
}
}
if resp.Events[0].Revision < lastMemberWatchRevision {
t.Errorf("Error watch is not sequential, expect: %v or higher, got: %v, member id: %v", lastMemberWatchRevision, resp.Events[0].Revision, resp.MemberId)
}
}
}
}
}
Loading