github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/watch/watch_test.go (about)

     1  package watch
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/docker/go-events"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestTimeoutLimitWatch(t *testing.T) {
    14  	require := require.New(t)
    15  	q := NewQueue(WithTimeout(time.Second), WithLimit(5), WithCloseOutChan())
    16  	defer q.Close()
    17  	ctx, cancel := context.WithCancel(context.Background())
    18  
    19  	// Cancelling a watcher's context should remove the watcher from the queue and
    20  	// close its channel.
    21  	doneChan := make(chan struct{})
    22  	go func() {
    23  		events := q.WatchContext(ctx)
    24  		for range events {
    25  		}
    26  		close(doneChan)
    27  	}()
    28  	cancel()
    29  	<-doneChan
    30  
    31  	// Test a scenario with a faster write rate than read rate The queue
    32  	// should eventually fill up and the channel will be closed.
    33  	readerSleepDuration := 100 * time.Millisecond
    34  	writerSleepDuration := 10 * time.Millisecond
    35  
    36  	events, cancel := q.Watch()
    37  	defer cancel()
    38  
    39  	receivedChan := make(chan struct{})
    40  	eventsClosed := make(chan struct{})
    41  
    42  	go func() {
    43  		closed := false
    44  		for range events {
    45  			if !closed {
    46  				close(receivedChan)
    47  				closed = true
    48  			}
    49  			time.Sleep(readerSleepDuration)
    50  		}
    51  		close(eventsClosed)
    52  	}()
    53  
    54  	// Publish one event and wait for the watcher to receive it
    55  	q.Publish("new event")
    56  	<-receivedChan
    57  
    58  	timeoutTimer := time.NewTimer(time.Minute)
    59  selectLoop:
    60  	for {
    61  		select {
    62  		case <-timeoutTimer.C:
    63  			require.Fail("Timeout exceeded")
    64  		case <-time.After(writerSleepDuration):
    65  			q.Publish("new event")
    66  		case <-eventsClosed:
    67  			break selectLoop
    68  		}
    69  	}
    70  
    71  	_, ok := <-events
    72  	require.False(ok)
    73  }
    74  
    75  func TestWatch(t *testing.T) {
    76  	// Create a queue
    77  	q := NewQueue()
    78  	defer q.Close()
    79  
    80  	type testEvent struct {
    81  		tags []string
    82  		str  string
    83  	}
    84  
    85  	tagFilter := func(t string) events.Matcher {
    86  		return events.MatcherFunc(func(event events.Event) bool {
    87  			testEvent := event.(testEvent)
    88  			for _, itemTag := range testEvent.tags {
    89  				if t == itemTag {
    90  					return true
    91  				}
    92  			}
    93  			return false
    94  		})
    95  	}
    96  
    97  	// Create filtered watchers
    98  	c1, c1cancel := q.CallbackWatch(tagFilter("t1"))
    99  	defer c1cancel()
   100  	c2, c2cancel := q.CallbackWatch(tagFilter("t2"))
   101  	defer c2cancel()
   102  
   103  	// Publish items on the queue
   104  	q.Publish(testEvent{tags: []string{"t1"}, str: "foo"})
   105  	q.Publish(testEvent{tags: []string{"t2"}, str: "bar"})
   106  	q.Publish(testEvent{tags: []string{"t1", "t2"}, str: "foobar"})
   107  	q.Publish(testEvent{tags: []string{"t3"}, str: "baz"})
   108  
   109  	if (<-c1).(testEvent).str != "foo" {
   110  		t.Fatal(`expected "foo" on c1`)
   111  	}
   112  
   113  	ev := (<-c1).(testEvent)
   114  	if ev.str != "foobar" {
   115  		t.Fatal(`expected "foobar" on c1`, ev)
   116  	}
   117  	if (<-c2).(testEvent).str != "bar" {
   118  		t.Fatal(`expected "bar" on c2`)
   119  	}
   120  	if (<-c2).(testEvent).str != "foobar" {
   121  		t.Fatal(`expected "foobar" on c2`)
   122  	}
   123  
   124  	c1cancel()
   125  
   126  	select {
   127  	case _, ok := <-c1:
   128  		if ok {
   129  			t.Fatal("unexpected value on c1")
   130  		}
   131  	default:
   132  		// operation does not proceed after cancel
   133  	}
   134  
   135  	q.Publish(testEvent{tags: []string{"t1", "t2"}, str: "foobar"})
   136  
   137  	if (<-c2).(testEvent).str != "foobar" {
   138  		t.Fatal(`expected "foobar" on c2`)
   139  	}
   140  
   141  	c2cancel()
   142  
   143  	select {
   144  	case _, ok := <-c2:
   145  		if ok {
   146  			t.Fatal("unexpected value on c2")
   147  		}
   148  	default:
   149  		// operation does not proceed after cancel
   150  	}
   151  }
   152  
   153  func BenchmarkPublish10(b *testing.B) {
   154  	benchmarkWatch(b, 10, 1, false)
   155  }
   156  
   157  func BenchmarkPublish100(b *testing.B) {
   158  	benchmarkWatch(b, 100, 1, false)
   159  }
   160  
   161  func BenchmarkPublish1000(b *testing.B) {
   162  	benchmarkWatch(b, 1000, 1, false)
   163  }
   164  
   165  func BenchmarkPublish10000(b *testing.B) {
   166  	benchmarkWatch(b, 10000, 1, false)
   167  }
   168  
   169  func BenchmarkPublish10Listeners4Publishers(b *testing.B) {
   170  	benchmarkWatch(b, 10, 4, false)
   171  }
   172  
   173  func BenchmarkPublish100Listeners8Publishers(b *testing.B) {
   174  	benchmarkWatch(b, 100, 8, false)
   175  }
   176  
   177  func BenchmarkPublish1000Listeners4Publishers(b *testing.B) {
   178  	benchmarkWatch(b, 1000, 4, false)
   179  }
   180  
   181  func BenchmarkPublish1000Listeners64Publishers(b *testing.B) {
   182  	benchmarkWatch(b, 1000, 64, false)
   183  }
   184  
   185  func BenchmarkWatch10(b *testing.B) {
   186  	benchmarkWatch(b, 10, 1, true)
   187  }
   188  
   189  func BenchmarkWatch100(b *testing.B) {
   190  	benchmarkWatch(b, 100, 1, true)
   191  }
   192  
   193  func BenchmarkWatch1000(b *testing.B) {
   194  	benchmarkWatch(b, 1000, 1, true)
   195  }
   196  
   197  func BenchmarkWatch10000(b *testing.B) {
   198  	benchmarkWatch(b, 10000, 1, true)
   199  }
   200  
   201  func BenchmarkWatch10Listeners4Publishers(b *testing.B) {
   202  	benchmarkWatch(b, 10, 4, true)
   203  }
   204  
   205  func BenchmarkWatch100Listeners8Publishers(b *testing.B) {
   206  	benchmarkWatch(b, 100, 8, true)
   207  }
   208  
   209  func BenchmarkWatch1000Listeners4Publishers(b *testing.B) {
   210  	benchmarkWatch(b, 1000, 4, true)
   211  }
   212  
   213  func BenchmarkWatch1000Listeners64Publishers(b *testing.B) {
   214  	benchmarkWatch(b, 1000, 64, true)
   215  }
   216  
   217  func benchmarkWatch(b *testing.B, nlisteners, npublishers int, waitForWatchers bool) {
   218  	q := NewQueue()
   219  	defer q.Close()
   220  	benchmarkWatchForQueue(q, b, nlisteners, npublishers, waitForWatchers)
   221  }
   222  
   223  func benchmarkWatchForQueue(q *Queue, b *testing.B, nlisteners, npublishers int, waitForWatchers bool) {
   224  	var (
   225  		watchersAttached  sync.WaitGroup
   226  		watchersRunning   sync.WaitGroup
   227  		publishersRunning sync.WaitGroup
   228  	)
   229  
   230  	for i := 0; i < nlisteners; i++ {
   231  		watchersAttached.Add(1)
   232  		watchersRunning.Add(1)
   233  		go func(n int) {
   234  			w, cancel := q.Watch()
   235  			defer cancel()
   236  			watchersAttached.Done()
   237  
   238  			for i := 0; i != n; i++ {
   239  				<-w
   240  			}
   241  			if waitForWatchers {
   242  				watchersRunning.Done()
   243  			}
   244  		}(b.N / npublishers * npublishers)
   245  	}
   246  
   247  	// Wait for watchers to be in place before we start publishing events.
   248  	watchersAttached.Wait()
   249  
   250  	b.ResetTimer()
   251  
   252  	for i := 0; i < npublishers; i++ {
   253  		publishersRunning.Add(1)
   254  		go func(n int) {
   255  			for i := 0; i < n; i++ {
   256  				q.Publish("myevent")
   257  			}
   258  			publishersRunning.Done()
   259  		}(b.N / npublishers)
   260  	}
   261  
   262  	publishersRunning.Wait()
   263  
   264  	if waitForWatchers {
   265  		watchersRunning.Wait()
   266  	}
   267  }