Skip to content
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

Use strict synchronization for revision getter to minimize flaky result caused by time racing. #17513

Closed
wants to merge 1 commit into from
Closed
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
6 changes: 5 additions & 1 deletion client/pkg/testutil/recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package testutil
import (
"errors"
"fmt"
"math"
"sync"
"time"
)
Expand Down Expand Up @@ -115,7 +116,10 @@ func (r *recorderStream) Chan() <-chan Action {

func (r *recorderStream) Wait(n int) ([]Action, error) {
acts := make([]Action, n)
timeoutC := time.After(r.waitTimeout)
var timeoutC <-chan time.Time
if r.waitTimeout < math.MaxInt64 {
timeoutC = time.After(r.waitTimeout)
}
Comment on lines +119 to +122
Copy link
Member

Choose a reason for hiding this comment

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

Suggest to follow the pattern "A Timeout of zero means no timeout."

FYI.
https://github.com/golang/go/blob/c2c4a32f9e57ac9f7102deeba8273bcd2b205d3c/src/net/http/client.go#L96

Copy link
Contributor

Choose a reason for hiding this comment

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

@joshuazh-x @ahrtr I'd like to address this comment as we are seeing TestPeriodic tests flaking frequently in our CI. Please let me know if I can proceed or if there are any other plans to take it forward.

Copy link
Member

Choose a reason for hiding this comment

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

If there is no feedback from @joshuazh-x next week, please feel free to create a new PR. thx

Copy link
Contributor

Choose a reason for hiding this comment

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

@ahrtr I am creating a new PR then with similar changes and review comments addressed. TY !

Copy link
Member

Choose a reason for hiding this comment

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

OK, thanks

Copy link
Contributor

Choose a reason for hiding this comment

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

I have cherry-picked the change and addressed review comments here, #18608 . Could you please take a look ?
thanks !

for i := 0; i < n; i++ {
select {
case acts[i] = <-r.ch:
Expand Down
39 changes: 23 additions & 16 deletions server/etcdserver/api/v3compactor/periodic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package v3compactor

import (
"errors"
"math"
"reflect"
"testing"
"time"
Expand All @@ -33,7 +34,7 @@ func TestPeriodicHourly(t *testing.T) {

fc := clockwork.NewFakeClock()
// TODO: Do not depand or real time (Recorder.Wait) in unit tests.
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond), 0}
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(math.MaxInt64), 0}
compactable := &fakeCompactable{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond)}
tb := newPeriodic(zaptest.NewLogger(t), fc, retentionDuration, rg, compactable)

Expand All @@ -43,8 +44,8 @@ func TestPeriodicHourly(t *testing.T) {
initialIntervals, intervalsPerPeriod := tb.getRetentions(), 10

// compaction doesn't happen til 2 hours elapse
for i := 0; i < initialIntervals; i++ {
rg.Wait(1)
for i := 0; i < initialIntervals-1; i++ {
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -63,7 +64,7 @@ func TestPeriodicHourly(t *testing.T) {
for i := 0; i < 3; i++ {
// advance one hour, one revision for each interval
for j := 0; j < intervalsPerPeriod; j++ {
rg.Wait(1)
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -84,7 +85,7 @@ func TestPeriodicMinutes(t *testing.T) {
retentionDuration := time.Duration(retentionMinutes) * time.Minute

fc := clockwork.NewFakeClock()
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond), 0}
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(math.MaxInt64), 0}
compactable := &fakeCompactable{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond)}
tb := newPeriodic(zaptest.NewLogger(t), fc, retentionDuration, rg, compactable)

Expand All @@ -94,8 +95,8 @@ func TestPeriodicMinutes(t *testing.T) {
initialIntervals, intervalsPerPeriod := tb.getRetentions(), 10

// compaction doesn't happen til 5 minutes elapse
for i := 0; i < initialIntervals; i++ {
rg.Wait(1)
for i := 0; i < initialIntervals-1; i++ {
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -113,7 +114,7 @@ func TestPeriodicMinutes(t *testing.T) {
for i := 0; i < 5; i++ {
// advance 5-minute, one revision for each interval
for j := 0; j < intervalsPerPeriod; j++ {
rg.Wait(1)
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -132,7 +133,7 @@ func TestPeriodicMinutes(t *testing.T) {
func TestPeriodicPause(t *testing.T) {
fc := clockwork.NewFakeClock()
retentionDuration := time.Hour
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond), 0}
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(math.MaxInt64), 0}
compactable := &fakeCompactable{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond)}
tb := newPeriodic(zaptest.NewLogger(t), fc, retentionDuration, rg, compactable)

Expand All @@ -143,7 +144,7 @@ func TestPeriodicPause(t *testing.T) {

// tb will collect 3 hours of revisions but not compact since paused
for i := 0; i < n*3; i++ {
rg.Wait(1)
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}
// t.revs = [21 22 23 24 25 26 27 28 29 30]
Expand All @@ -156,7 +157,7 @@ func TestPeriodicPause(t *testing.T) {

// tb resumes to being blocked on the clock
tb.Resume()
rg.Wait(1)
waitOneAction(t, rg)

// unblock clock, will kick off a compaction at T=3h6m by retry
fc.Advance(tb.getRetryInterval())
Expand All @@ -179,7 +180,7 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {
retentionDuration := time.Duration(retentionMinutes) * time.Minute

fc := clockwork.NewFakeClock()
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond), 0}
rg := &fakeRevGetter{testutil.NewRecorderStreamWithWaitTimout(math.MaxInt64), 0}
compactable := &fakeCompactable{testutil.NewRecorderStreamWithWaitTimout(10 * time.Millisecond)}
tb := newPeriodic(zaptest.NewLogger(t), fc, retentionDuration, rg, compactable)

Expand All @@ -189,10 +190,10 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {
initialIntervals, intervalsPerPeriod := tb.getRetentions(), 10

// first compaction happens til 5 minutes elapsed
for i := 0; i < initialIntervals; i++ {
for i := 0; i < initialIntervals-1; i++ {
// every time set the same revision with 100
rg.SetRev(int64(100))
rg.Wait(1)
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -212,7 +213,7 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {
for i := 0; i < 5; i++ {
for j := 0; j < intervalsPerPeriod; j++ {
rg.SetRev(int64(100))
rg.Wait(1)
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -224,7 +225,7 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {

// when revision changed, compaction is normally
for i := 0; i < initialIntervals; i++ {
rg.Wait(1)
waitOneAction(t, rg)
fc.Advance(tb.getRetryInterval())
}

Expand All @@ -238,3 +239,9 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {
t.Errorf("compact request = %v, want %v", a[0].Params[0], &pb.CompactionRequest{Revision: expectedRevision})
}
}

func waitOneAction(t *testing.T, r testutil.Recorder) {
if actions, _ := r.Wait(1); len(actions) != 1 {
t.Errorf("expect 1 action, got %v instead", len(actions))
}
}
Loading