get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/ipqueue_test.go (about)

     1  // Copyright 2021 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"sync"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  func TestIPQueueBasic(t *testing.T) {
    23  	s := &Server{}
    24  	q := newIPQueue[int](s, "test")
    25  	// Check that pool has been created
    26  	if q.pool == nil {
    27  		t.Fatal("Expected pool to have been created")
    28  	}
    29  	// Check for the default mrs
    30  	if q.mrs != ipQueueDefaultMaxRecycleSize {
    31  		t.Fatalf("Expected default max recycle size to be %v, got %v",
    32  			ipQueueDefaultMaxRecycleSize, q.mrs)
    33  	}
    34  	select {
    35  	case <-q.ch:
    36  		t.Fatalf("Should not have been notified")
    37  	default:
    38  		// OK!
    39  	}
    40  	if l := q.len(); l != 0 {
    41  		t.Fatalf("Expected len to be 0, got %v", l)
    42  	}
    43  
    44  	// Try to change the max recycle size
    45  	q2 := newIPQueue[int](s, "test2", ipQueue_MaxRecycleSize(10))
    46  	if q2.mrs != 10 {
    47  		t.Fatalf("Expected max recycle size to be 10, got %v", q2.mrs)
    48  	}
    49  
    50  	// Check that those 2 queues are registered
    51  	var gotFirst bool
    52  	var gotSecond bool
    53  	s.ipQueues.Range(func(k, v interface{}) bool {
    54  		switch k.(string) {
    55  		case "test":
    56  			gotFirst = true
    57  		case "test2":
    58  			gotSecond = true
    59  		default:
    60  			t.Fatalf("Unknown queue: %q", k.(string))
    61  		}
    62  		return true
    63  	})
    64  	if !gotFirst {
    65  		t.Fatalf("Did not find queue %q", "test")
    66  	}
    67  	if !gotSecond {
    68  		t.Fatalf("Did not find queue %q", "test2")
    69  	}
    70  	// Unregister them
    71  	q.unregister()
    72  	q2.unregister()
    73  	// They should have been removed from the map
    74  	s.ipQueues.Range(func(k, v interface{}) bool {
    75  		t.Fatalf("Got queue %q", k.(string))
    76  		return false
    77  	})
    78  	// But verify that we can still push/pop
    79  	q.push(1)
    80  	elts := q.pop()
    81  	if len(elts) != 1 {
    82  		t.Fatalf("Should have gotten 1 element, got %v", len(elts))
    83  	}
    84  	q2.push(2)
    85  	if e, ok := q2.popOne(); !ok || e != 2 {
    86  		t.Fatalf("popOne failed: %+v", e)
    87  	}
    88  }
    89  
    90  func TestIPQueuePush(t *testing.T) {
    91  	s := &Server{}
    92  	q := newIPQueue[int](s, "test")
    93  	q.push(1)
    94  	if l := q.len(); l != 1 {
    95  		t.Fatalf("Expected len to be 1, got %v", l)
    96  	}
    97  	select {
    98  	case <-q.ch:
    99  		// OK
   100  	default:
   101  		t.Fatalf("Should have been notified of addition")
   102  	}
   103  	// Push a new element, we should not be notified.
   104  	q.push(2)
   105  	if l := q.len(); l != 2 {
   106  		t.Fatalf("Expected len to be 2, got %v", l)
   107  	}
   108  	select {
   109  	case <-q.ch:
   110  		t.Fatalf("Should not have been notified of addition")
   111  	default:
   112  		// OK
   113  	}
   114  }
   115  
   116  func TestIPQueuePop(t *testing.T) {
   117  	s := &Server{}
   118  	q := newIPQueue[int](s, "test")
   119  	q.push(1)
   120  	<-q.ch
   121  	elts := q.pop()
   122  	if l := len(elts); l != 1 {
   123  		t.Fatalf("Expected 1 elt, got %v", l)
   124  	}
   125  	if l := q.len(); l != 0 {
   126  		t.Fatalf("Expected len to be 0, got %v", l)
   127  	}
   128  	// The channel notification should be empty
   129  	select {
   130  	case <-q.ch:
   131  		t.Fatalf("Should not have been notified of addition")
   132  	default:
   133  		// OK
   134  	}
   135  	// Since pop() brings the number of pending to 0, we keep track of the
   136  	// number of "in progress" elements. Check that the value is 1 here.
   137  	if n := q.inProgress(); n != 1 {
   138  		t.Fatalf("Expected count to be 1, got %v", n)
   139  	}
   140  	// Recycling will bring it down to 0.
   141  	q.recycle(&elts)
   142  	if n := q.inProgress(); n != 0 {
   143  		t.Fatalf("Expected count to be 0, got %v", n)
   144  	}
   145  	// If we call pop() now, we should get an empty list.
   146  	if elts = q.pop(); elts != nil {
   147  		t.Fatalf("Expected nil, got %v", elts)
   148  	}
   149  	// The in progress count should still be 0
   150  	if n := q.inProgress(); n != 0 {
   151  		t.Fatalf("Expected count to be 0, got %v", n)
   152  	}
   153  }
   154  
   155  func TestIPQueuePopOne(t *testing.T) {
   156  	s := &Server{}
   157  	q := newIPQueue[int](s, "test")
   158  	q.push(1)
   159  	<-q.ch
   160  	e, ok := q.popOne()
   161  	if !ok {
   162  		t.Fatal("Got nil")
   163  	}
   164  	if i := e; i != 1 {
   165  		t.Fatalf("Expected 1, got %v", i)
   166  	}
   167  	if l := q.len(); l != 0 {
   168  		t.Fatalf("Expected len to be 0, got %v", l)
   169  	}
   170  	// That does not affect the number of notProcessed
   171  	if n := q.inProgress(); n != 0 {
   172  		t.Fatalf("Expected count to be 0, got %v", n)
   173  	}
   174  	select {
   175  	case <-q.ch:
   176  		t.Fatalf("Should not have been notified of addition")
   177  	default:
   178  		// OK
   179  	}
   180  	q.push(2)
   181  	q.push(3)
   182  	e, ok = q.popOne()
   183  	if !ok {
   184  		t.Fatal("Got nil")
   185  	}
   186  	if i := e; i != 2 {
   187  		t.Fatalf("Expected 2, got %v", i)
   188  	}
   189  	if l := q.len(); l != 1 {
   190  		t.Fatalf("Expected len to be 1, got %v", l)
   191  	}
   192  	select {
   193  	case <-q.ch:
   194  		// OK
   195  	default:
   196  		t.Fatalf("Should have been notified that there is more")
   197  	}
   198  	e, ok = q.popOne()
   199  	if !ok {
   200  		t.Fatal("Got nil")
   201  	}
   202  	if i := e; i != 3 {
   203  		t.Fatalf("Expected 3, got %v", i)
   204  	}
   205  	if l := q.len(); l != 0 {
   206  		t.Fatalf("Expected len to be 0, got %v", l)
   207  	}
   208  	select {
   209  	case <-q.ch:
   210  		t.Fatalf("Should not have been notified that there is more")
   211  	default:
   212  		// OK
   213  	}
   214  	// Calling it again now that we know there is nothing, we
   215  	// should get nil.
   216  	if e, ok = q.popOne(); ok {
   217  		t.Fatalf("Expected nil, got %v", e)
   218  	}
   219  
   220  	q = newIPQueue[int](s, "test2")
   221  	q.push(1)
   222  	q.push(2)
   223  	// Capture current capacity
   224  	q.Lock()
   225  	c := cap(q.elts)
   226  	q.Unlock()
   227  	e, ok = q.popOne()
   228  	if !ok || e != 1 {
   229  		t.Fatalf("Invalid value: %v", e)
   230  	}
   231  	if l := q.len(); l != 1 {
   232  		t.Fatalf("Expected len to be 1, got %v", l)
   233  	}
   234  	values := q.pop()
   235  	if len(values) != 1 || values[0] != 2 {
   236  		t.Fatalf("Unexpected values: %v", values)
   237  	}
   238  	if cap(values) != c-1 {
   239  		t.Fatalf("Unexpected capacity: %v vs %v", cap(values), c-1)
   240  	}
   241  	if l := q.len(); l != 0 {
   242  		t.Fatalf("Expected len to be 0, got %v", l)
   243  	}
   244  	// Just make sure that this is ok...
   245  	q.recycle(&values)
   246  }
   247  
   248  func TestIPQueueMultiProducers(t *testing.T) {
   249  	s := &Server{}
   250  	q := newIPQueue[int](s, "test")
   251  
   252  	wg := sync.WaitGroup{}
   253  	wg.Add(3)
   254  	send := func(start, end int) {
   255  		defer wg.Done()
   256  
   257  		for i := start; i <= end; i++ {
   258  			q.push(i)
   259  		}
   260  	}
   261  	go send(1, 100)
   262  	go send(101, 200)
   263  	go send(201, 300)
   264  
   265  	tm := time.NewTimer(2 * time.Second)
   266  	m := make(map[int]struct{})
   267  	for done := false; !done; {
   268  		select {
   269  		case <-q.ch:
   270  			values := q.pop()
   271  			for _, v := range values {
   272  				m[v] = struct{}{}
   273  			}
   274  			q.recycle(&values)
   275  			if n := q.inProgress(); n != 0 {
   276  				t.Fatalf("Expected count to be 0, got %v", n)
   277  			}
   278  			done = len(m) == 300
   279  		case <-tm.C:
   280  			t.Fatalf("Did not receive all elements: %v", m)
   281  		}
   282  	}
   283  	wg.Wait()
   284  }
   285  
   286  func TestIPQueueRecycle(t *testing.T) {
   287  	s := &Server{}
   288  	q := newIPQueue[int](s, "test")
   289  	total := 1000
   290  	for iter := 0; iter < 5; iter++ {
   291  		var sz int
   292  		for i := 0; i < total; i++ {
   293  			sz = q.push(i)
   294  		}
   295  		if sz != total {
   296  			t.Fatalf("Expected size to be %v, got %v", total, sz)
   297  		}
   298  		values := q.pop()
   299  		preRecycleCap := cap(values)
   300  		q.recycle(&values)
   301  		sz = q.push(1001)
   302  		if sz != 1 {
   303  			t.Fatalf("Expected size to be %v, got %v", 1, sz)
   304  		}
   305  		values = q.pop()
   306  		if l := len(values); l != 1 {
   307  			t.Fatalf("Len should be 1, got %v", l)
   308  		}
   309  		if c := cap(values); c == preRecycleCap {
   310  			break
   311  		} else if iter == 4 {
   312  			// We can't fail the test since there is no guarantee that the slice
   313  			// is still present in the pool when we do a Get(), but let's log that
   314  			// recycling did not occur even after all iterations..
   315  			t.Logf("Seem like the previous slice was not recycled, old cap=%v new cap=%v",
   316  				preRecycleCap, c)
   317  		}
   318  	}
   319  
   320  	q = newIPQueue[int](s, "test2", ipQueue_MaxRecycleSize(10))
   321  	for i := 0; i < 100; i++ {
   322  		q.push(i)
   323  	}
   324  	values := q.pop()
   325  	preRecycleCap := cap(values)
   326  	q.recycle(&values)
   327  	q.push(1001)
   328  	values = q.pop()
   329  	if l := len(values); l != 1 {
   330  		t.Fatalf("Len should be 1, got %v", l)
   331  	}
   332  	// This time, we should not have recycled it, so the new cap should
   333  	// be 1 for the new element added. In case Go creates a slice of
   334  	// cap more than 1 in some future release, just check that the
   335  	// cap is lower than the pre recycle cap.
   336  	if c := cap(values); c >= preRecycleCap {
   337  		t.Fatalf("The slice should not have been put back in the pool, got cap of %v", c)
   338  	}
   339  
   340  	// Also check that if we mistakenly pop a queue that was not
   341  	// notified (pop() will return nil), and we try to recycle,
   342  	// recycle() will ignore the call.
   343  	values = q.pop()
   344  	q.recycle(&values)
   345  	q.push(1002)
   346  	q.Lock()
   347  	recycled := &q.elts == &values
   348  	q.Unlock()
   349  	if recycled {
   350  		t.Fatalf("Unexpected recycled slice")
   351  	}
   352  	// Check that we don't crash when recycling a nil or empty slice
   353  	values = q.pop()
   354  	q.recycle(&values)
   355  	q.recycle(nil)
   356  }
   357  
   358  func TestIPQueueDrain(t *testing.T) {
   359  	s := &Server{}
   360  	q := newIPQueue[int](s, "test")
   361  	for iter, recycled := 0, false; iter < 5 && !recycled; iter++ {
   362  		for i := 0; i < 100; i++ {
   363  			q.push(i + 1)
   364  		}
   365  		q.drain()
   366  		// Try to get something from the pool right away
   367  		s := q.pool.Get()
   368  		recycled := s != nil
   369  		if !recycled {
   370  			// We can't fail the test, since we have no guarantee it will be recycled
   371  			// especially when running with `-race` flag...
   372  			if iter == 4 {
   373  				t.Log("nothing was recycled")
   374  			}
   375  		}
   376  		// Check that we have consumed the signal...
   377  		select {
   378  		case <-q.ch:
   379  			t.Fatal("Signal should have been consumed by drain")
   380  		default:
   381  			// OK!
   382  		}
   383  		// Check len
   384  		if l := q.len(); l != 0 {
   385  			t.Fatalf("Expected len to be 0, got %v", l)
   386  		}
   387  		if recycled {
   388  			break
   389  		}
   390  	}
   391  }