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 }