github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/sublist_test.go (about) 1 // Copyright 2016-2023 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 "errors" 18 "flag" 19 "fmt" 20 "math/rand" 21 "os" 22 "runtime" 23 "strconv" 24 "strings" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/nats-io/nuid" 30 ) 31 32 func stackFatalf(t *testing.T, f string, args ...any) { 33 lines := make([]string, 0, 32) 34 msg := fmt.Sprintf(f, args...) 35 lines = append(lines, msg) 36 37 // Generate the Stack of callers: Skip us and verify* frames. 38 for i := 2; true; i++ { 39 _, file, line, ok := runtime.Caller(i) 40 if !ok { 41 break 42 } 43 msg := fmt.Sprintf("%d - %s:%d", i, file, line) 44 lines = append(lines, msg) 45 } 46 t.Fatalf("%s", strings.Join(lines, "\n")) 47 } 48 49 func verifyCount(s *Sublist, count uint32, t *testing.T) { 50 t.Helper() 51 if s.Count() != count { 52 t.Fatalf("Count is %d, should be %d", s.Count(), count) 53 } 54 } 55 56 func verifyLen(r []*subscription, l int, t *testing.T) { 57 t.Helper() 58 if len(r) != l { 59 t.Fatalf("Results len is %d, should be %d", len(r), l) 60 } 61 } 62 63 func verifyQLen(r [][]*subscription, l int, t *testing.T) { 64 t.Helper() 65 if len(r) != l { 66 t.Fatalf("Queue Results len is %d, should be %d", len(r), l) 67 } 68 } 69 70 func verifyNumLevels(s *Sublist, expected int, t *testing.T) { 71 t.Helper() 72 dl := s.numLevels() 73 if dl != expected { 74 t.Fatalf("NumLevels is %d, should be %d", dl, expected) 75 } 76 } 77 78 func verifyQMember(qsubs [][]*subscription, val *subscription, t *testing.T) { 79 t.Helper() 80 verifyMember(qsubs[findQSlot(val.queue, qsubs)], val, t) 81 } 82 83 func verifyMember(r []*subscription, val *subscription, t *testing.T) { 84 t.Helper() 85 for _, v := range r { 86 if v == nil { 87 continue 88 } 89 if v == val { 90 return 91 } 92 } 93 t.Fatalf("Subscription (%p) for [%s : %s] not found in results", val, val.subject, val.queue) 94 } 95 96 // Helpers to generate test subscriptions. 97 func newSub(subject string) *subscription { 98 c := &client{kind: CLIENT} 99 return &subscription{client: c, subject: []byte(subject)} 100 } 101 102 func newQSub(subject, queue string) *subscription { 103 if queue != "" { 104 return &subscription{subject: []byte(subject), queue: []byte(queue)} 105 } 106 return newSub(subject) 107 } 108 109 func newRemoteQSub(subject, queue string, num int32) *subscription { 110 if queue != "" { 111 c := &client{kind: ROUTER} 112 return &subscription{client: c, subject: []byte(subject), queue: []byte(queue), qw: num} 113 } 114 return newSub(subject) 115 } 116 117 func TestSublistInit(t *testing.T) { 118 s := NewSublistWithCache() 119 verifyCount(s, 0, t) 120 } 121 122 func TestSublistInsertCount(t *testing.T) { 123 testSublistInsertCount(t, NewSublistWithCache()) 124 } 125 126 func TestSublistInsertCountNoCache(t *testing.T) { 127 testSublistInsertCount(t, NewSublistNoCache()) 128 } 129 130 func testSublistInsertCount(t *testing.T, s *Sublist) { 131 s.Insert(newSub("foo")) 132 s.Insert(newSub("bar")) 133 s.Insert(newSub("foo.bar")) 134 verifyCount(s, 3, t) 135 } 136 137 func TestSublistSimple(t *testing.T) { 138 testSublistSimple(t, NewSublistWithCache()) 139 } 140 141 func TestSublistSimpleNoCache(t *testing.T) { 142 testSublistSimple(t, NewSublistNoCache()) 143 } 144 145 func testSublistSimple(t *testing.T, s *Sublist) { 146 subject := "foo" 147 sub := newSub(subject) 148 s.Insert(sub) 149 r := s.Match(subject) 150 verifyLen(r.psubs, 1, t) 151 verifyMember(r.psubs, sub, t) 152 } 153 154 func TestSublistSimpleMultiTokens(t *testing.T) { 155 testSublistSimpleMultiTokens(t, NewSublistWithCache()) 156 } 157 158 func TestSublistSimpleMultiTokensNoCache(t *testing.T) { 159 testSublistSimpleMultiTokens(t, NewSublistNoCache()) 160 } 161 162 func testSublistSimpleMultiTokens(t *testing.T, s *Sublist) { 163 subject := "foo.bar.baz" 164 sub := newSub(subject) 165 s.Insert(sub) 166 r := s.Match(subject) 167 verifyLen(r.psubs, 1, t) 168 verifyMember(r.psubs, sub, t) 169 } 170 171 func TestSublistPartialWildcard(t *testing.T) { 172 testSublistPartialWildcard(t, NewSublistWithCache()) 173 } 174 175 func TestSublistPartialWildcardNoCache(t *testing.T) { 176 testSublistPartialWildcard(t, NewSublistNoCache()) 177 } 178 179 func testSublistPartialWildcard(t *testing.T, s *Sublist) { 180 lsub := newSub("a.b.c") 181 psub := newSub("a.*.c") 182 s.Insert(lsub) 183 s.Insert(psub) 184 r := s.Match("a.b.c") 185 verifyLen(r.psubs, 2, t) 186 verifyMember(r.psubs, lsub, t) 187 verifyMember(r.psubs, psub, t) 188 } 189 190 func TestSublistPartialWildcardAtEnd(t *testing.T) { 191 testSublistPartialWildcardAtEnd(t, NewSublistWithCache()) 192 } 193 194 func TestSublistPartialWildcardAtEndNoCache(t *testing.T) { 195 testSublistPartialWildcardAtEnd(t, NewSublistNoCache()) 196 } 197 198 func testSublistPartialWildcardAtEnd(t *testing.T, s *Sublist) { 199 lsub := newSub("a.b.c") 200 psub := newSub("a.b.*") 201 s.Insert(lsub) 202 s.Insert(psub) 203 r := s.Match("a.b.c") 204 verifyLen(r.psubs, 2, t) 205 verifyMember(r.psubs, lsub, t) 206 verifyMember(r.psubs, psub, t) 207 } 208 209 func TestSublistFullWildcard(t *testing.T) { 210 testSublistFullWildcard(t, NewSublistWithCache()) 211 } 212 213 func TestSublistFullWildcardNoCache(t *testing.T) { 214 testSublistFullWildcard(t, NewSublistNoCache()) 215 } 216 217 func testSublistFullWildcard(t *testing.T, s *Sublist) { 218 lsub := newSub("a.b.c") 219 fsub := newSub("a.>") 220 s.Insert(lsub) 221 s.Insert(fsub) 222 r := s.Match("a.b.c") 223 verifyLen(r.psubs, 2, t) 224 verifyMember(r.psubs, lsub, t) 225 verifyMember(r.psubs, fsub, t) 226 227 r = s.Match("a.>") 228 verifyLen(r.psubs, 1, t) 229 verifyMember(r.psubs, fsub, t) 230 } 231 232 func TestSublistRemove(t *testing.T) { 233 testSublistRemove(t, NewSublistWithCache()) 234 } 235 236 func TestSublistRemoveNoCache(t *testing.T) { 237 testSublistRemove(t, NewSublistNoCache()) 238 } 239 240 func testSublistRemove(t *testing.T, s *Sublist) { 241 subject := "a.b.c.d" 242 sub := newSub(subject) 243 s.Insert(sub) 244 verifyCount(s, 1, t) 245 r := s.Match(subject) 246 verifyLen(r.psubs, 1, t) 247 s.Remove(newSub("a.b.c")) 248 verifyCount(s, 1, t) 249 s.Remove(sub) 250 verifyCount(s, 0, t) 251 r = s.Match(subject) 252 verifyLen(r.psubs, 0, t) 253 } 254 255 func TestSublistRemoveWildcard(t *testing.T) { 256 testSublistRemoveWildcard(t, NewSublistWithCache()) 257 } 258 259 func TestSublistRemoveWildcardNoCache(t *testing.T) { 260 testSublistRemoveWildcard(t, NewSublistNoCache()) 261 } 262 263 func testSublistRemoveWildcard(t *testing.T, s *Sublist) { 264 subject := "a.b.c.d" 265 sub := newSub(subject) 266 psub := newSub("a.b.*.d") 267 fsub := newSub("a.b.>") 268 s.Insert(sub) 269 s.Insert(psub) 270 s.Insert(fsub) 271 verifyCount(s, 3, t) 272 r := s.Match(subject) 273 verifyLen(r.psubs, 3, t) 274 s.Remove(sub) 275 verifyCount(s, 2, t) 276 s.Remove(fsub) 277 verifyCount(s, 1, t) 278 s.Remove(psub) 279 verifyCount(s, 0, t) 280 r = s.Match(subject) 281 verifyLen(r.psubs, 0, t) 282 } 283 284 func TestSublistRemoveCleanup(t *testing.T) { 285 testSublistRemoveCleanup(t, NewSublistWithCache()) 286 } 287 288 func TestSublistRemoveCleanupNoCache(t *testing.T) { 289 testSublistRemoveCleanup(t, NewSublistNoCache()) 290 } 291 292 func testSublistRemoveCleanup(t *testing.T, s *Sublist) { 293 literal := "a.b.c.d.e.f" 294 depth := len(strings.Split(literal, tsep)) 295 sub := newSub(literal) 296 verifyNumLevels(s, 0, t) 297 s.Insert(sub) 298 verifyNumLevels(s, depth, t) 299 s.Remove(sub) 300 verifyNumLevels(s, 0, t) 301 } 302 303 func TestSublistRemoveCleanupWildcards(t *testing.T) { 304 testSublistRemoveCleanupWildcards(t, NewSublistWithCache()) 305 } 306 307 func TestSublistRemoveCleanupWildcardsNoCache(t *testing.T) { 308 testSublistRemoveCleanupWildcards(t, NewSublistNoCache()) 309 } 310 311 func testSublistRemoveCleanupWildcards(t *testing.T, s *Sublist) { 312 subject := "a.b.*.d.e.>" 313 depth := len(strings.Split(subject, tsep)) 314 sub := newSub(subject) 315 verifyNumLevels(s, 0, t) 316 s.Insert(sub) 317 verifyNumLevels(s, depth, t) 318 s.Remove(sub) 319 verifyNumLevels(s, 0, t) 320 } 321 322 func TestSublistRemoveWithLargeSubs(t *testing.T) { 323 testSublistRemoveWithLargeSubs(t, NewSublistWithCache()) 324 } 325 326 func TestSublistRemoveWithLargeSubsNoCache(t *testing.T) { 327 testSublistRemoveWithLargeSubs(t, NewSublistNoCache()) 328 } 329 330 func testSublistRemoveWithLargeSubs(t *testing.T, s *Sublist) { 331 subject := "foo" 332 for i := 0; i < plistMin*2; i++ { 333 sub := newSub(subject) 334 s.Insert(sub) 335 } 336 r := s.Match(subject) 337 verifyLen(r.psubs, plistMin*2, t) 338 // Remove one that is in the middle 339 s.Remove(r.psubs[plistMin]) 340 // Remove first one 341 s.Remove(r.psubs[0]) 342 // Remove last one 343 s.Remove(r.psubs[len(r.psubs)-1]) 344 // Check len again 345 r = s.Match(subject) 346 verifyLen(r.psubs, plistMin*2-3, t) 347 } 348 349 func TestSublistInvalidSubjectsInsert(t *testing.T) { 350 testSublistInvalidSubjectsInsert(t, NewSublistWithCache()) 351 } 352 353 func TestSublistInvalidSubjectsInsertNoCache(t *testing.T) { 354 testSublistInvalidSubjectsInsert(t, NewSublistNoCache()) 355 } 356 357 func TestSublistNoCacheRemoveBatch(t *testing.T) { 358 s := NewSublistNoCache() 359 s.Insert(newSub("foo")) 360 sub := newSub("bar") 361 s.Insert(sub) 362 s.RemoveBatch([]*subscription{sub}) 363 // Now test that this did not turn on cache 364 for i := 0; i < 10; i++ { 365 s.Match("foo") 366 } 367 if s.CacheEnabled() { 368 t.Fatalf("Cache should not be enabled") 369 } 370 } 371 372 func TestSublistRemoveBatchWithError(t *testing.T) { 373 s := NewSublistNoCache() 374 sub1 := newSub("foo") 375 sub2 := newSub("bar") 376 sub3 := newSub("baz") 377 s.Insert(sub1) 378 s.Insert(sub2) 379 s.Insert(sub3) 380 subNotPresent := newSub("not.inserted") 381 // Try to remove all subs, but include the sub that has not been inserted. 382 err := s.RemoveBatch([]*subscription{subNotPresent, sub1, sub3}) 383 // We expect an error to be returned, but sub1,2 and 3 to have been removed. 384 require_Error(t, err, ErrNotFound) 385 // Make sure that we have only sub2 present 386 verifyCount(s, 1, t) 387 r := s.Match("bar") 388 verifyLen(r.psubs, 1, t) 389 verifyMember(r.psubs, sub2, t) 390 r = s.Match("foo") 391 verifyLen(r.psubs, 0, t) 392 r = s.Match("baz") 393 verifyLen(r.psubs, 0, t) 394 } 395 396 func testSublistInvalidSubjectsInsert(t *testing.T, s *Sublist) { 397 // Insert, or subscriptions, can have wildcards, but not empty tokens, 398 // and can not have a FWC that is not the terminal token. 399 400 // beginning empty token 401 if err := s.Insert(newSub(".foo")); err != ErrInvalidSubject { 402 t.Fatal("Expected invalid subject error") 403 } 404 405 // trailing empty token 406 if err := s.Insert(newSub("foo.")); err != ErrInvalidSubject { 407 t.Fatal("Expected invalid subject error") 408 } 409 // empty middle token 410 if err := s.Insert(newSub("foo..bar")); err != ErrInvalidSubject { 411 t.Fatal("Expected invalid subject error") 412 } 413 // empty middle token #2 414 if err := s.Insert(newSub("foo.bar..baz")); err != ErrInvalidSubject { 415 t.Fatal("Expected invalid subject error") 416 } 417 // fwc not terminal 418 if err := s.Insert(newSub("foo.>.bar")); err != ErrInvalidSubject { 419 t.Fatal("Expected invalid subject error") 420 } 421 } 422 423 func TestSublistCache(t *testing.T) { 424 s := NewSublistWithCache() 425 426 // Test add a remove logistics 427 subject := "a.b.c.d" 428 sub := newSub(subject) 429 psub := newSub("a.b.*.d") 430 fsub := newSub("a.b.>") 431 s.Insert(sub) 432 r := s.Match(subject) 433 verifyLen(r.psubs, 1, t) 434 s.Insert(psub) 435 s.Insert(fsub) 436 verifyCount(s, 3, t) 437 r = s.Match(subject) 438 verifyLen(r.psubs, 3, t) 439 s.Remove(sub) 440 verifyCount(s, 2, t) 441 s.Remove(fsub) 442 verifyCount(s, 1, t) 443 s.Remove(psub) 444 verifyCount(s, 0, t) 445 446 // Check that cache is now empty 447 if cc := s.CacheCount(); cc != 0 { 448 t.Fatalf("Cache should be zero, got %d\n", cc) 449 } 450 451 r = s.Match(subject) 452 verifyLen(r.psubs, 0, t) 453 454 for i := 0; i < 2*slCacheMax; i++ { 455 s.Match(fmt.Sprintf("foo-%d\n", i)) 456 } 457 458 checkFor(t, 2*time.Second, 10*time.Millisecond, func() error { 459 if cc := s.CacheCount(); cc > slCacheMax { 460 return fmt.Errorf("Cache should be constrained by cacheMax, got %d for current count", cc) 461 } 462 return nil 463 }) 464 465 // Test that adding to a wildcard properly adds to the cache. 466 s = NewSublistWithCache() 467 s.Insert(newSub("foo.*")) 468 s.Insert(newSub("foo.bar")) 469 r = s.Match("foo.baz") 470 verifyLen(r.psubs, 1, t) 471 r = s.Match("foo.bar") 472 verifyLen(r.psubs, 2, t) 473 s.Insert(newSub("foo.>")) 474 r = s.Match("foo.bar") 475 verifyLen(r.psubs, 3, t) 476 } 477 478 func TestSublistBasicQueueResults(t *testing.T) { 479 testSublistBasicQueueResults(t, NewSublistWithCache()) 480 } 481 482 func TestSublistBasicQueueResultsNoCache(t *testing.T) { 483 testSublistBasicQueueResults(t, NewSublistNoCache()) 484 } 485 486 func testSublistBasicQueueResults(t *testing.T, s *Sublist) { 487 // Test some basics 488 subject := "foo" 489 sub := newSub(subject) 490 sub1 := newQSub(subject, "bar") 491 sub2 := newQSub(subject, "baz") 492 493 s.Insert(sub1) 494 r := s.Match(subject) 495 verifyLen(r.psubs, 0, t) 496 verifyQLen(r.qsubs, 1, t) 497 verifyLen(r.qsubs[0], 1, t) 498 verifyQMember(r.qsubs, sub1, t) 499 500 s.Insert(sub2) 501 r = s.Match(subject) 502 verifyLen(r.psubs, 0, t) 503 verifyQLen(r.qsubs, 2, t) 504 verifyLen(r.qsubs[0], 1, t) 505 verifyLen(r.qsubs[1], 1, t) 506 verifyQMember(r.qsubs, sub1, t) 507 verifyQMember(r.qsubs, sub2, t) 508 509 s.Insert(sub) 510 r = s.Match(subject) 511 verifyLen(r.psubs, 1, t) 512 verifyQLen(r.qsubs, 2, t) 513 verifyLen(r.qsubs[0], 1, t) 514 verifyLen(r.qsubs[1], 1, t) 515 verifyQMember(r.qsubs, sub1, t) 516 verifyQMember(r.qsubs, sub2, t) 517 verifyMember(r.psubs, sub, t) 518 519 sub3 := newQSub(subject, "bar") 520 sub4 := newQSub(subject, "baz") 521 522 s.Insert(sub3) 523 s.Insert(sub4) 524 525 r = s.Match(subject) 526 verifyLen(r.psubs, 1, t) 527 verifyQLen(r.qsubs, 2, t) 528 verifyLen(r.qsubs[0], 2, t) 529 verifyLen(r.qsubs[1], 2, t) 530 verifyQMember(r.qsubs, sub1, t) 531 verifyQMember(r.qsubs, sub2, t) 532 verifyQMember(r.qsubs, sub3, t) 533 verifyQMember(r.qsubs, sub4, t) 534 verifyMember(r.psubs, sub, t) 535 536 // Now removal 537 s.Remove(sub) 538 539 r = s.Match(subject) 540 verifyLen(r.psubs, 0, t) 541 verifyQLen(r.qsubs, 2, t) 542 verifyLen(r.qsubs[0], 2, t) 543 verifyLen(r.qsubs[1], 2, t) 544 verifyQMember(r.qsubs, sub1, t) 545 verifyQMember(r.qsubs, sub2, t) 546 547 s.Remove(sub1) 548 r = s.Match(subject) 549 verifyLen(r.psubs, 0, t) 550 verifyQLen(r.qsubs, 2, t) 551 verifyLen(r.qsubs[findQSlot(sub1.queue, r.qsubs)], 1, t) 552 verifyLen(r.qsubs[findQSlot(sub2.queue, r.qsubs)], 2, t) 553 verifyQMember(r.qsubs, sub2, t) 554 verifyQMember(r.qsubs, sub3, t) 555 verifyQMember(r.qsubs, sub4, t) 556 557 s.Remove(sub3) // Last one 558 r = s.Match(subject) 559 verifyLen(r.psubs, 0, t) 560 verifyQLen(r.qsubs, 1, t) 561 verifyLen(r.qsubs[0], 2, t) // this is sub2/baz now 562 verifyQMember(r.qsubs, sub2, t) 563 564 s.Remove(sub2) 565 s.Remove(sub4) 566 r = s.Match(subject) 567 verifyLen(r.psubs, 0, t) 568 verifyQLen(r.qsubs, 0, t) 569 } 570 571 func checkBool(b, expected bool, t *testing.T) { 572 t.Helper() 573 if b != expected { 574 t.Fatalf("Expected %v, but got %v\n", expected, b) 575 } 576 } 577 578 func checkError(err, expected error, t *testing.T) { 579 t.Helper() 580 if err != expected && err != nil && !errors.Is(err, expected) { 581 t.Fatalf("Expected %v, but got %v\n", expected, err) 582 } 583 } 584 585 func TestSublistValidLiteralSubjects(t *testing.T) { 586 checkBool(IsValidLiteralSubject("foo"), true, t) 587 checkBool(IsValidLiteralSubject(".foo"), false, t) 588 checkBool(IsValidLiteralSubject("foo."), false, t) 589 checkBool(IsValidLiteralSubject("foo..bar"), false, t) 590 checkBool(IsValidLiteralSubject("foo.bar.*"), false, t) 591 checkBool(IsValidLiteralSubject("foo.bar.>"), false, t) 592 checkBool(IsValidLiteralSubject("*"), false, t) 593 checkBool(IsValidLiteralSubject(">"), false, t) 594 // The followings have widlcards characters but are not 595 // considered as such because they are not individual tokens. 596 checkBool(IsValidLiteralSubject("foo*"), true, t) 597 checkBool(IsValidLiteralSubject("foo**"), true, t) 598 checkBool(IsValidLiteralSubject("foo.**"), true, t) 599 checkBool(IsValidLiteralSubject("foo*bar"), true, t) 600 checkBool(IsValidLiteralSubject("foo.*bar"), true, t) 601 checkBool(IsValidLiteralSubject("foo*.bar"), true, t) 602 checkBool(IsValidLiteralSubject("*bar"), true, t) 603 checkBool(IsValidLiteralSubject("foo>"), true, t) 604 checkBool(IsValidLiteralSubject("foo>>"), true, t) 605 checkBool(IsValidLiteralSubject("foo.>>"), true, t) 606 checkBool(IsValidLiteralSubject("foo>bar"), true, t) 607 checkBool(IsValidLiteralSubject("foo.>bar"), true, t) 608 checkBool(IsValidLiteralSubject("foo>.bar"), true, t) 609 checkBool(IsValidLiteralSubject(">bar"), true, t) 610 } 611 612 func TestSublistValidSubjects(t *testing.T) { 613 checkBool(IsValidSubject("."), false, t) 614 checkBool(IsValidSubject(".foo"), false, t) 615 checkBool(IsValidSubject("foo."), false, t) 616 checkBool(IsValidSubject("foo..bar"), false, t) 617 checkBool(IsValidSubject(">.bar"), false, t) 618 checkBool(IsValidSubject("foo.>.bar"), false, t) 619 checkBool(IsValidSubject("foo"), true, t) 620 checkBool(IsValidSubject("foo.bar.*"), true, t) 621 checkBool(IsValidSubject("foo.bar.>"), true, t) 622 checkBool(IsValidSubject("*"), true, t) 623 checkBool(IsValidSubject(">"), true, t) 624 checkBool(IsValidSubject("foo*"), true, t) 625 checkBool(IsValidSubject("foo**"), true, t) 626 checkBool(IsValidSubject("foo.**"), true, t) 627 checkBool(IsValidSubject("foo*bar"), true, t) 628 checkBool(IsValidSubject("foo.*bar"), true, t) 629 checkBool(IsValidSubject("foo*.bar"), true, t) 630 checkBool(IsValidSubject("*bar"), true, t) 631 checkBool(IsValidSubject("foo>"), true, t) 632 checkBool(IsValidSubject("foo.>>"), true, t) 633 checkBool(IsValidSubject("foo>bar"), true, t) 634 checkBool(IsValidSubject("foo.>bar"), true, t) 635 checkBool(IsValidSubject("foo>.bar"), true, t) 636 checkBool(IsValidSubject(">bar"), true, t) 637 } 638 639 func TestSublistMatchLiterals(t *testing.T) { 640 checkBool(matchLiteral("foo", "foo"), true, t) 641 checkBool(matchLiteral("foo", "bar"), false, t) 642 checkBool(matchLiteral("foo", "*"), true, t) 643 checkBool(matchLiteral("foo", ">"), true, t) 644 checkBool(matchLiteral("foo.bar", ">"), true, t) 645 checkBool(matchLiteral("foo.bar", "foo.>"), true, t) 646 checkBool(matchLiteral("foo.bar", "bar.>"), false, t) 647 checkBool(matchLiteral("stats.test.22", "stats.>"), true, t) 648 checkBool(matchLiteral("stats.test.22", "stats.*.*"), true, t) 649 checkBool(matchLiteral("foo.bar", "foo"), false, t) 650 checkBool(matchLiteral("stats.test.foos", "stats.test.foos"), true, t) 651 checkBool(matchLiteral("stats.test.foos", "stats.test.foo"), false, t) 652 checkBool(matchLiteral("stats.test", "stats.test.*"), false, t) 653 checkBool(matchLiteral("stats.test.foos", "stats.*"), false, t) 654 checkBool(matchLiteral("stats.test.foos", "stats.*.*.foos"), false, t) 655 656 // These are cases where wildcards characters should not be considered 657 // wildcards since they do not follow the rules of wildcards. 658 checkBool(matchLiteral("*bar", "*bar"), true, t) 659 checkBool(matchLiteral("foo*", "foo*"), true, t) 660 checkBool(matchLiteral("foo*bar", "foo*bar"), true, t) 661 checkBool(matchLiteral("foo.***.bar", "foo.***.bar"), true, t) 662 checkBool(matchLiteral(">bar", ">bar"), true, t) 663 checkBool(matchLiteral("foo>", "foo>"), true, t) 664 checkBool(matchLiteral("foo>bar", "foo>bar"), true, t) 665 checkBool(matchLiteral("foo.>>>.bar", "foo.>>>.bar"), true, t) 666 } 667 668 func TestSubjectIsLiteral(t *testing.T) { 669 checkBool(subjectIsLiteral("foo"), true, t) 670 checkBool(subjectIsLiteral("foo.bar"), true, t) 671 checkBool(subjectIsLiteral("foo*.bar"), true, t) 672 checkBool(subjectIsLiteral("*"), false, t) 673 checkBool(subjectIsLiteral(">"), false, t) 674 checkBool(subjectIsLiteral("foo.*"), false, t) 675 checkBool(subjectIsLiteral("foo.>"), false, t) 676 checkBool(subjectIsLiteral("foo.*.>"), false, t) 677 checkBool(subjectIsLiteral("foo.*.bar"), false, t) 678 checkBool(subjectIsLiteral("foo.bar.>"), false, t) 679 } 680 681 func TestValidateDestinationSubject(t *testing.T) { 682 checkError(ValidateMappingDestination("foo"), nil, t) 683 checkError(ValidateMappingDestination("foo.bar"), nil, t) 684 checkError(ValidateMappingDestination("*"), nil, t) 685 checkError(ValidateMappingDestination(">"), nil, t) 686 checkError(ValidateMappingDestination("foo.*"), nil, t) 687 checkError(ValidateMappingDestination("foo.>"), nil, t) 688 checkError(ValidateMappingDestination("foo.*.>"), nil, t) 689 checkError(ValidateMappingDestination("foo.*.bar"), nil, t) 690 checkError(ValidateMappingDestination("foo.bar.>"), nil, t) 691 checkError(ValidateMappingDestination("foo.{{wildcard(1)}}"), nil, t) 692 checkError(ValidateMappingDestination("foo.{{ wildcard(1) }}"), nil, t) 693 checkError(ValidateMappingDestination("foo.{{wildcard( 1 )}}"), nil, t) 694 checkError(ValidateMappingDestination("foo.{{partition(2,1)}}"), nil, t) 695 checkError(ValidateMappingDestination("foo.{{SplitFromLeft(2,1)}}"), nil, t) 696 checkError(ValidateMappingDestination("foo.{{SplitFromRight(2,1)}}"), nil, t) 697 checkError(ValidateMappingDestination("foo.{{unknown(1)}}"), ErrInvalidMappingDestination, t) 698 checkError(ValidateMappingDestination("foo..}"), ErrInvalidMappingDestination, t) 699 checkError(ValidateMappingDestination("foo. bar}"), ErrInvalidMappingDestinationSubject, t) 700 701 } 702 703 func TestSubjectToken(t *testing.T) { 704 checkToken := func(token, expected string) { 705 t.Helper() 706 if token != expected { 707 t.Fatalf("Expected token of %q, got %q", expected, token) 708 } 709 } 710 checkToken(tokenAt("foo.bar.baz.*", 0), "") 711 checkToken(tokenAt("foo.bar.baz.*", 1), "foo") 712 checkToken(tokenAt("foo.bar.baz.*", 2), "bar") 713 checkToken(tokenAt("foo.bar.baz.*", 3), "baz") 714 checkToken(tokenAt("foo.bar.baz.*", 4), "*") 715 checkToken(tokenAt("foo.bar.baz.*", 5), "") 716 } 717 718 func TestSublistBadSubjectOnRemove(t *testing.T) { 719 testSublistBadSubjectOnRemove(t, NewSublistWithCache()) 720 } 721 722 func TestSublistBadSubjectOnRemoveNoCache(t *testing.T) { 723 testSublistBadSubjectOnRemove(t, NewSublistNoCache()) 724 } 725 726 func testSublistBadSubjectOnRemove(t *testing.T, s *Sublist) { 727 bad := "a.b..d" 728 sub := newSub(bad) 729 730 if err := s.Insert(sub); err != ErrInvalidSubject { 731 t.Fatalf("Expected ErrInvalidSubject, got %v\n", err) 732 } 733 734 if err := s.Remove(sub); err != ErrInvalidSubject { 735 t.Fatalf("Expected ErrInvalidSubject, got %v\n", err) 736 } 737 738 badfwc := "a.>.b" 739 if err := s.Remove(newSub(badfwc)); err != ErrInvalidSubject { 740 t.Fatalf("Expected ErrInvalidSubject, got %v\n", err) 741 } 742 } 743 744 // This is from bug report #18 745 func TestSublistTwoTokenPubMatchSingleTokenSub(t *testing.T) { 746 testSublistTwoTokenPubMatchSingleTokenSub(t, NewSublistWithCache()) 747 } 748 749 func TestSublistTwoTokenPubMatchSingleTokenSubNoCache(t *testing.T) { 750 testSublistTwoTokenPubMatchSingleTokenSub(t, NewSublistNoCache()) 751 } 752 753 func testSublistTwoTokenPubMatchSingleTokenSub(t *testing.T, s *Sublist) { 754 sub := newSub("foo") 755 s.Insert(sub) 756 r := s.Match("foo") 757 verifyLen(r.psubs, 1, t) 758 verifyMember(r.psubs, sub, t) 759 r = s.Match("foo.bar") 760 verifyLen(r.psubs, 0, t) 761 } 762 763 func TestSublistInsertWithWildcardsAsLiterals(t *testing.T) { 764 testSublistInsertWithWildcardsAsLiterals(t, NewSublistWithCache()) 765 } 766 767 func TestSublistInsertWithWildcardsAsLiteralsNoCache(t *testing.T) { 768 testSublistInsertWithWildcardsAsLiterals(t, NewSublistNoCache()) 769 } 770 771 func testSublistInsertWithWildcardsAsLiterals(t *testing.T, s *Sublist) { 772 subjects := []string{"foo.*-", "foo.>-"} 773 for _, subject := range subjects { 774 sub := newSub(subject) 775 s.Insert(sub) 776 // Should find no match 777 r := s.Match("foo.bar") 778 verifyLen(r.psubs, 0, t) 779 // Should find a match 780 r = s.Match(subject) 781 verifyLen(r.psubs, 1, t) 782 } 783 } 784 785 func TestSublistRemoveWithWildcardsAsLiterals(t *testing.T) { 786 testSublistRemoveWithWildcardsAsLiterals(t, NewSublistWithCache()) 787 } 788 789 func TestSublistRemoveWithWildcardsAsLiteralsNoCache(t *testing.T) { 790 testSublistRemoveWithWildcardsAsLiterals(t, NewSublistNoCache()) 791 } 792 793 func testSublistRemoveWithWildcardsAsLiterals(t *testing.T, s *Sublist) { 794 subjects := []string{"foo.*-", "foo.>-"} 795 for _, subject := range subjects { 796 sub := newSub(subject) 797 s.Insert(sub) 798 // Should find no match 799 rsub := newSub("foo.bar") 800 s.Remove(rsub) 801 if c := s.Count(); c != 1 { 802 t.Fatalf("Expected sublist to still contain sub, got %v", c) 803 } 804 s.Remove(sub) 805 if c := s.Count(); c != 0 { 806 t.Fatalf("Expected sublist to be empty, got %v", c) 807 } 808 } 809 } 810 811 func TestSublistRaceOnRemove(t *testing.T) { 812 testSublistRaceOnRemove(t, NewSublistWithCache()) 813 } 814 815 func TestSublistRaceOnRemoveNoCache(t *testing.T) { 816 testSublistRaceOnRemove(t, NewSublistNoCache()) 817 } 818 819 func testSublistRaceOnRemove(t *testing.T, s *Sublist) { 820 var ( 821 total = 100 822 subs = make(map[int]*subscription, total) // use map for randomness 823 ) 824 for i := 0; i < total; i++ { 825 sub := newQSub("foo", "bar") 826 subs[i] = sub 827 } 828 829 for i := 0; i < 2; i++ { 830 for _, sub := range subs { 831 s.Insert(sub) 832 } 833 // Call Match() once or twice, to make sure we get from cache 834 if i == 1 { 835 s.Match("foo") 836 } 837 // This will be from cache when i==1 838 r := s.Match("foo") 839 wg := sync.WaitGroup{} 840 wg.Add(1) 841 go func() { 842 for _, sub := range subs { 843 s.Remove(sub) 844 } 845 wg.Done() 846 }() 847 for _, qsub := range r.qsubs { 848 for i := 0; i < len(qsub); i++ { 849 sub := qsub[i] 850 if string(sub.queue) != "bar" { 851 t.Fatalf("Queue name should be bar, got %s", qsub[i].queue) 852 } 853 } 854 } 855 wg.Wait() 856 } 857 858 // Repeat tests with regular subs 859 for i := 0; i < total; i++ { 860 sub := newSub("foo") 861 subs[i] = sub 862 } 863 864 for i := 0; i < 2; i++ { 865 for _, sub := range subs { 866 s.Insert(sub) 867 } 868 // Call Match() once or twice, to make sure we get from cache 869 if i == 1 { 870 s.Match("foo") 871 } 872 // This will be from cache when i==1 873 r := s.Match("foo") 874 wg := sync.WaitGroup{} 875 wg.Add(1) 876 go func() { 877 for _, sub := range subs { 878 s.Remove(sub) 879 } 880 wg.Done() 881 }() 882 for i := 0; i < len(r.psubs); i++ { 883 sub := r.psubs[i] 884 if string(sub.subject) != "foo" { 885 t.Fatalf("Subject should be foo, got %s", sub.subject) 886 } 887 } 888 wg.Wait() 889 } 890 } 891 892 func TestSublistRaceOnInsert(t *testing.T) { 893 testSublistRaceOnInsert(t, NewSublistWithCache()) 894 } 895 896 func TestSublistRaceOnInsertNoCache(t *testing.T) { 897 testSublistRaceOnInsert(t, NewSublistNoCache()) 898 } 899 900 func testSublistRaceOnInsert(t *testing.T, s *Sublist) { 901 var ( 902 total = 100 903 subs = make(map[int]*subscription, total) // use map for randomness 904 wg sync.WaitGroup 905 ) 906 for i := 0; i < total; i++ { 907 sub := newQSub("foo", "bar") 908 subs[i] = sub 909 } 910 wg.Add(1) 911 go func() { 912 for _, sub := range subs { 913 s.Insert(sub) 914 } 915 wg.Done() 916 }() 917 for i := 0; i < 1000; i++ { 918 r := s.Match("foo") 919 for _, qsubs := range r.qsubs { 920 for _, qsub := range qsubs { 921 if string(qsub.queue) != "bar" { 922 t.Fatalf("Expected queue name to be bar, got %v", string(qsub.queue)) 923 } 924 } 925 } 926 } 927 wg.Wait() 928 929 // Repeat the test with plain subs 930 for i := 0; i < total; i++ { 931 sub := newSub("foo") 932 subs[i] = sub 933 } 934 wg.Add(1) 935 go func() { 936 for _, sub := range subs { 937 s.Insert(sub) 938 } 939 wg.Done() 940 }() 941 for i := 0; i < 1000; i++ { 942 r := s.Match("foo") 943 for _, sub := range r.psubs { 944 if string(sub.subject) != "foo" { 945 t.Fatalf("Expected subject to be foo, got %v", string(sub.subject)) 946 } 947 } 948 } 949 wg.Wait() 950 } 951 952 func TestSublistRaceOnMatch(t *testing.T) { 953 s := NewSublistNoCache() 954 s.Insert(newQSub("foo.*", "workers")) 955 s.Insert(newQSub("foo.bar", "workers")) 956 s.Insert(newSub("foo.*")) 957 s.Insert(newSub("foo.bar")) 958 959 wg := sync.WaitGroup{} 960 wg.Add(2) 961 errCh := make(chan error, 2) 962 f := func() { 963 defer wg.Done() 964 for i := 0; i < 10; i++ { 965 r := s.Match("foo.bar") 966 for _, sub := range r.psubs { 967 if !strings.HasPrefix(string(sub.subject), "foo.") { 968 errCh <- fmt.Errorf("Wrong subject: %s", sub.subject) 969 return 970 } 971 } 972 for _, qsub := range r.qsubs { 973 for _, sub := range qsub { 974 if string(sub.queue) != "workers" { 975 errCh <- fmt.Errorf("Wrong queue name: %s", sub.queue) 976 return 977 } 978 } 979 } 980 } 981 } 982 go f() 983 go f() 984 wg.Wait() 985 select { 986 case e := <-errCh: 987 t.Fatalf(e.Error()) 988 default: 989 } 990 } 991 992 // Remote subscriptions for queue subscribers will be weighted such that a single subscription 993 // is received, but represents all of the queue subscribers on the remote side. 994 func TestSublistRemoteQueueSubscriptions(t *testing.T) { 995 testSublistRemoteQueueSubscriptions(t, NewSublistWithCache()) 996 } 997 998 func TestSublistRemoteQueueSubscriptionsNoCache(t *testing.T) { 999 testSublistRemoteQueueSubscriptions(t, NewSublistNoCache()) 1000 } 1001 1002 func testSublistRemoteQueueSubscriptions(t *testing.T, s *Sublist) { 1003 // Normals 1004 s1 := newQSub("foo", "bar") 1005 s2 := newQSub("foo", "bar") 1006 s.Insert(s1) 1007 s.Insert(s2) 1008 1009 // Now do weighted remotes. 1010 rs1 := newRemoteQSub("foo", "bar", 10) 1011 s.Insert(rs1) 1012 rs2 := newRemoteQSub("foo", "bar", 10) 1013 s.Insert(rs2) 1014 1015 // These are just shadowed in results, so should appear as 4 subs. 1016 verifyCount(s, 4, t) 1017 1018 r := s.Match("foo") 1019 verifyLen(r.psubs, 0, t) 1020 verifyQLen(r.qsubs, 1, t) 1021 verifyLen(r.qsubs[0], 22, t) 1022 1023 s.Remove(s1) 1024 s.Remove(rs1) 1025 1026 verifyCount(s, 2, t) 1027 1028 // Now make sure our shadowed results are correct after a removal. 1029 r = s.Match("foo") 1030 verifyLen(r.psubs, 0, t) 1031 verifyQLen(r.qsubs, 1, t) 1032 verifyLen(r.qsubs[0], 11, t) 1033 1034 // Now do an update to an existing remote sub to update its weight. 1035 rs2.qw = 1 1036 s.UpdateRemoteQSub(rs2) 1037 1038 // Results should reflect new weight. 1039 r = s.Match("foo") 1040 verifyLen(r.psubs, 0, t) 1041 verifyQLen(r.qsubs, 1, t) 1042 verifyLen(r.qsubs[0], 2, t) 1043 } 1044 1045 func TestSublistSharedEmptyResult(t *testing.T) { 1046 s := NewSublistWithCache() 1047 r1 := s.Match("foo") 1048 verifyLen(r1.psubs, 0, t) 1049 verifyQLen(r1.qsubs, 0, t) 1050 1051 r2 := s.Match("bar") 1052 verifyLen(r2.psubs, 0, t) 1053 verifyQLen(r2.qsubs, 0, t) 1054 1055 if r1 != r2 { 1056 t.Fatalf("Expected empty result to be a shared result set") 1057 } 1058 } 1059 1060 func TestSublistNoCacheStats(t *testing.T) { 1061 s := NewSublistNoCache() 1062 s.Insert(newSub("foo")) 1063 s.Insert(newSub("bar")) 1064 s.Insert(newSub("baz")) 1065 s.Insert(newSub("foo.bar.baz")) 1066 s.Match("a.b.c") 1067 s.Match("bar") 1068 stats := s.Stats() 1069 if stats.NumCache != 0 { 1070 t.Fatalf("Expected 0 for NumCache stat, got %d", stats.NumCache) 1071 } 1072 } 1073 1074 func TestSublistAll(t *testing.T) { 1075 s := NewSublistNoCache() 1076 subs := []*subscription{ 1077 newSub("foo.bar.baz"), 1078 newSub("foo"), 1079 newSub("baz"), 1080 } 1081 // alter client's kind 1082 subs[0].client.kind = LEAF 1083 for _, sub := range subs { 1084 s.Insert(sub) 1085 } 1086 1087 var buf [32]*subscription 1088 output := buf[:0] 1089 s.All(&output) 1090 if len(output) != len(subs) { 1091 t.Fatalf("Expected %d for All, got %d", len(subs), len(output)) 1092 } 1093 } 1094 1095 func TestIsSubsetMatch(t *testing.T) { 1096 for _, test := range []struct { 1097 subject string 1098 test string 1099 result bool 1100 }{ 1101 {"foo.bar", "foo.bar", true}, 1102 {"foo.*", ">", true}, 1103 {"foo.*", "*.*", true}, 1104 {"foo.*", "foo.*", true}, 1105 {"foo.*", "foo.bar", false}, 1106 {"foo.>", ">", true}, 1107 {"foo.>", "*.>", true}, 1108 {"foo.>", "foo.>", true}, 1109 {"foo.>", "foo.bar", false}, 1110 {"foo..bar", "foo.*", false}, // Bad subject, we return false 1111 {"foo.*", "foo..bar", false}, // Bad subject, we return false 1112 } { 1113 t.Run("", func(t *testing.T) { 1114 if res := subjectIsSubsetMatch(test.subject, test.test); res != test.result { 1115 t.Fatalf("Subject %q subset match of %q, should be %v, got %v", 1116 test.test, test.subject, test.result, res) 1117 } 1118 }) 1119 } 1120 } 1121 1122 func TestSublistRegisterInterestNotification(t *testing.T) { 1123 s := NewSublistWithCache() 1124 ch := make(chan bool, 1) 1125 1126 expectErr := func(subject string) { 1127 if err := s.RegisterNotification(subject, ch); err != ErrInvalidSubject { 1128 t.Fatalf("Expected err, got %v", err) 1129 } 1130 } 1131 1132 // Test that we require a literal subject. 1133 expectErr("foo.*") 1134 expectErr(">") 1135 1136 // Chan needs to be non-nil 1137 if err := s.RegisterNotification("foo", nil); err != ErrNilChan { 1138 t.Fatalf("Expected err, got %v", err) 1139 } 1140 1141 // Clearing one that is not there will return false. 1142 if s.ClearNotification("foo", ch) { 1143 t.Fatalf("Expected to return false on non-existent notification entry") 1144 } 1145 1146 // This should work properly. 1147 if err := s.RegisterNotification("foo", ch); err != nil { 1148 t.Fatalf("Unexpected error: %v", err) 1149 } 1150 1151 tt := time.NewTimer(time.Second) 1152 expectBoolWithCh := func(ch chan bool, b bool) { 1153 t.Helper() 1154 tt.Reset(time.Second) 1155 defer tt.Stop() 1156 select { 1157 case v := <-ch: 1158 if v != b { 1159 t.Fatalf("Expected %v, got %v", b, v) 1160 } 1161 case <-tt.C: 1162 t.Fatalf("Timeout waiting for expected value") 1163 } 1164 } 1165 expectBool := func(b bool) { 1166 t.Helper() 1167 expectBoolWithCh(ch, b) 1168 } 1169 expectFalse := func() { 1170 t.Helper() 1171 expectBool(false) 1172 } 1173 expectTrue := func() { 1174 t.Helper() 1175 expectBool(true) 1176 } 1177 expectNone := func() { 1178 t.Helper() 1179 if lch := len(ch); lch != 0 { 1180 t.Fatalf("Expected no notifications, had %d and first was %v", lch, <-ch) 1181 } 1182 } 1183 expectOneWithCh := func(ch chan bool) { 1184 t.Helper() 1185 if len(ch) != 1 { 1186 t.Fatalf("Expected 1 notification") 1187 } 1188 } 1189 expectOne := func() { 1190 t.Helper() 1191 expectOneWithCh(ch) 1192 } 1193 1194 expectOne() 1195 expectFalse() 1196 sub := newSub("foo") 1197 s.Insert(sub) 1198 expectTrue() 1199 1200 sub2 := newSub("foo") 1201 s.Insert(sub2) 1202 expectNone() 1203 1204 if err := s.RegisterNotification("bar", ch); err != nil { 1205 t.Fatalf("Unexpected error: %v", err) 1206 } 1207 expectFalse() 1208 1209 sub3 := newSub("foo") 1210 s.Insert(sub3) 1211 expectNone() 1212 1213 // Now remove literals. 1214 s.Remove(sub) 1215 expectNone() 1216 s.Remove(sub2) 1217 expectNone() 1218 s.Remove(sub3) 1219 expectFalse() 1220 1221 if err := s.RegisterNotification("test.node", ch); err != nil { 1222 t.Fatalf("Unexpected error: %v", err) 1223 } 1224 expectOne() 1225 expectFalse() 1226 1227 tnSub1 := newSub("test.node.already.exist") 1228 s.Insert(tnSub1) 1229 expectNone() 1230 1231 tnSub2 := newSub("test.node") 1232 s.Insert(tnSub2) 1233 expectTrue() 1234 1235 tnSub3 := newSub("test.node") 1236 s.Insert(tnSub3) 1237 expectNone() 1238 1239 s.Remove(tnSub1) 1240 expectNone() 1241 s.Remove(tnSub2) 1242 expectNone() 1243 s.Remove(tnSub3) 1244 expectFalse() 1245 1246 if !s.ClearNotification("test.node", ch) { 1247 t.Fatalf("Expected to return true") 1248 } 1249 1250 sub4 := newSub("bar") 1251 s.Insert(sub4) 1252 expectTrue() 1253 1254 if !s.ClearNotification("bar", ch) { 1255 t.Fatalf("Expected to return true") 1256 } 1257 s.RLock() 1258 lnr := len(s.notify.remove) 1259 s.RUnlock() 1260 if lnr != 0 { 1261 t.Fatalf("Expected zero entries for remove notify, got %d", lnr) 1262 } 1263 if !s.ClearNotification("foo", ch) { 1264 t.Fatalf("Expected to return true") 1265 } 1266 s.RLock() 1267 notifyMap := s.notify 1268 s.RUnlock() 1269 if notifyMap != nil { 1270 t.Fatalf("Expected the notify map to be nil") 1271 } 1272 1273 // Let's do some wildcard checks. 1274 // Wildcards will not trigger interest. 1275 subpwc := newSub("*") 1276 s.Insert(subpwc) 1277 expectNone() 1278 1279 if err := s.RegisterNotification("foo", ch); err != nil { 1280 t.Fatalf("Unexpected error: %v", err) 1281 } 1282 expectFalse() 1283 1284 s.Insert(sub) 1285 expectTrue() 1286 1287 s.Remove(sub) 1288 expectFalse() 1289 1290 s.Remove(subpwc) 1291 expectNone() 1292 1293 subfwc := newSub(">") 1294 s.Insert(subfwc) 1295 expectNone() 1296 1297 s.Insert(subpwc) 1298 expectNone() 1299 1300 s.Remove(subpwc) 1301 expectNone() 1302 1303 s.Remove(subfwc) 1304 expectNone() 1305 1306 // Test batch 1307 subs := []*subscription{sub, sub2, sub3, sub4, subpwc, subfwc} 1308 for _, sub := range subs { 1309 s.Insert(sub) 1310 } 1311 expectTrue() 1312 1313 s.RemoveBatch(subs) 1314 expectOne() 1315 expectFalse() 1316 1317 // Test queue subs 1318 // We know make sure that if you have qualified a queue group it has to match, etc. 1319 // Also if you do not specify one they will not trigger. 1320 qsub := newQSub("foo.bar.baz", "1") 1321 s.Insert(qsub) 1322 expectNone() 1323 1324 if err := s.RegisterNotification("foo.bar.baz", ch); err != nil { 1325 t.Fatalf("Unexpected error: %v", err) 1326 } 1327 expectFalse() 1328 1329 wcqsub := newQSub("foo.bar.>", "1") 1330 s.Insert(wcqsub) 1331 expectNone() 1332 1333 s.Remove(qsub) 1334 expectNone() 1335 1336 s.Remove(wcqsub) 1337 expectNone() 1338 1339 s.Insert(wcqsub) 1340 expectNone() 1341 1342 if err := s.RegisterQueueNotification("queue.test.node", "q22", ch); err != nil { 1343 t.Fatalf("Unexpected error: %v", err) 1344 } 1345 expectOne() 1346 expectFalse() 1347 1348 qsub1 := newQSub("queue.test.node.already.exist", "queue") 1349 s.Insert(qsub1) 1350 expectNone() 1351 1352 qsub2 := newQSub("queue.test.node", "q22") 1353 s.Insert(qsub2) 1354 expectTrue() 1355 1356 qsub3 := newQSub("queue.test.node", "otherqueue") 1357 s.Insert(qsub3) 1358 expectNone() 1359 1360 qsub4 := newQSub("queue.different.node", "q22") 1361 s.Insert(qsub4) 1362 expectNone() 1363 1364 qsub5 := newQSub("queue.test.node", "q22") 1365 s.Insert(qsub5) 1366 expectNone() 1367 1368 s.Remove(qsub3) 1369 expectNone() 1370 s.Remove(qsub1) 1371 expectNone() 1372 s.Remove(qsub2) 1373 expectNone() 1374 s.Remove(qsub4) 1375 expectNone() 1376 s.Remove(qsub5) 1377 expectFalse() 1378 1379 if !s.ClearQueueNotification("queue.test.node", "q22", ch) { 1380 t.Fatalf("Expected to return true") 1381 } 1382 1383 if err := s.RegisterQueueNotification("some.subject", "queue1", ch); err != nil { 1384 t.Fatalf("Unexpected error: %v", err) 1385 } 1386 expectOne() 1387 expectFalse() 1388 1389 qsub1 = newQSub("some.subject", "queue1") 1390 s.Insert(qsub1) 1391 expectTrue() 1392 1393 // Create a second channel for this other queue 1394 ch2 := make(chan bool, 1) 1395 if err := s.RegisterQueueNotification("some.subject", "queue2", ch2); err != nil { 1396 t.Fatalf("Unexpected error: %v", err) 1397 } 1398 expectOneWithCh(ch2) 1399 expectBoolWithCh(ch2, false) 1400 1401 qsub2 = newQSub("some.subject", "queue2") 1402 s.Insert(qsub2) 1403 expectBoolWithCh(ch2, true) 1404 1405 // But we should not get notification on queue1 1406 expectNone() 1407 1408 s.Remove(qsub1) 1409 expectFalse() 1410 s.Remove(qsub2) 1411 expectBoolWithCh(ch2, false) 1412 1413 if !s.ClearQueueNotification("some.subject", "queue1", ch) { 1414 t.Fatalf("Expected to return true") 1415 } 1416 if !s.ClearQueueNotification("some.subject", "queue2", ch2) { 1417 t.Fatalf("Expected to return true") 1418 } 1419 1420 // Test non-blocking notifications. 1421 if err := s.RegisterNotification("bar", ch); err != nil { 1422 t.Fatalf("Unexpected error: %v", err) 1423 } 1424 1425 if err := s.RegisterNotification("baz", ch); err != nil { 1426 t.Fatalf("Unexpected error: %v", err) 1427 } 1428 1429 s.Insert(newSub("baz")) 1430 s.Insert(newSub("bar")) 1431 s.Insert(subpwc) 1432 expectOne() 1433 expectFalse() 1434 } 1435 1436 func TestSublistReverseMatch(t *testing.T) { 1437 s := NewSublistWithCache() 1438 fooSub := newSub("foo") 1439 barSub := newSub("bar") 1440 fooBarSub := newSub("foo.bar") 1441 fooBazSub := newSub("foo.baz") 1442 fooBarBazSub := newSub("foo.bar.baz") 1443 s.Insert(fooSub) 1444 s.Insert(barSub) 1445 s.Insert(fooBarSub) 1446 s.Insert(fooBazSub) 1447 s.Insert(fooBarBazSub) 1448 1449 r := s.ReverseMatch("foo") 1450 verifyLen(r.psubs, 1, t) 1451 verifyMember(r.psubs, fooSub, t) 1452 1453 r = s.ReverseMatch("bar") 1454 verifyLen(r.psubs, 1, t) 1455 verifyMember(r.psubs, barSub, t) 1456 1457 r = s.ReverseMatch("*") 1458 verifyLen(r.psubs, 2, t) 1459 verifyMember(r.psubs, fooSub, t) 1460 verifyMember(r.psubs, barSub, t) 1461 1462 r = s.ReverseMatch("baz") 1463 verifyLen(r.psubs, 0, t) 1464 1465 r = s.ReverseMatch("foo.*") 1466 verifyLen(r.psubs, 2, t) 1467 verifyMember(r.psubs, fooBarSub, t) 1468 verifyMember(r.psubs, fooBazSub, t) 1469 1470 r = s.ReverseMatch("*.*") 1471 verifyLen(r.psubs, 2, t) 1472 verifyMember(r.psubs, fooBarSub, t) 1473 verifyMember(r.psubs, fooBazSub, t) 1474 1475 r = s.ReverseMatch("*.bar") 1476 verifyLen(r.psubs, 1, t) 1477 verifyMember(r.psubs, fooBarSub, t) 1478 1479 r = s.ReverseMatch("*.baz") 1480 verifyLen(r.psubs, 1, t) 1481 verifyMember(r.psubs, fooBazSub, t) 1482 1483 r = s.ReverseMatch("bar.*") 1484 verifyLen(r.psubs, 0, t) 1485 1486 r = s.ReverseMatch("*.bat") 1487 verifyLen(r.psubs, 0, t) 1488 1489 r = s.ReverseMatch("foo.>") 1490 verifyLen(r.psubs, 3, t) 1491 verifyMember(r.psubs, fooBarSub, t) 1492 verifyMember(r.psubs, fooBazSub, t) 1493 verifyMember(r.psubs, fooBarBazSub, t) 1494 1495 r = s.ReverseMatch(">") 1496 verifyLen(r.psubs, 5, t) 1497 verifyMember(r.psubs, fooSub, t) 1498 verifyMember(r.psubs, barSub, t) 1499 verifyMember(r.psubs, fooBarSub, t) 1500 verifyMember(r.psubs, fooBazSub, t) 1501 verifyMember(r.psubs, fooBarBazSub, t) 1502 } 1503 1504 func TestSublistReverseMatchWider(t *testing.T) { 1505 s := NewSublistWithCache() 1506 sub := newSub("uplink.*.*.>") 1507 s.Insert(sub) 1508 1509 r := s.ReverseMatch("uplink.1.*.*.>") 1510 verifyLen(r.psubs, 1, t) 1511 verifyMember(r.psubs, sub, t) 1512 1513 r = s.ReverseMatch("uplink.1.2.3.>") 1514 verifyLen(r.psubs, 1, t) 1515 verifyMember(r.psubs, sub, t) 1516 } 1517 1518 func TestSublistMatchWithEmptyTokens(t *testing.T) { 1519 for _, test := range []struct { 1520 name string 1521 cache bool 1522 }{ 1523 {"cache", true}, 1524 {"no cache", false}, 1525 } { 1526 t.Run(test.name, func(t *testing.T) { 1527 sl := NewSublist(true) 1528 sub1 := newSub(">") 1529 sub2 := newQSub(">", "queue") 1530 sl.Insert(sub1) 1531 sl.Insert(sub2) 1532 1533 for _, subj := range []string{".foo", "..foo", "foo..", "foo.", "foo..bar", "foo...bar"} { 1534 t.Run(subj, func(t *testing.T) { 1535 r := sl.Match(subj) 1536 verifyLen(r.psubs, 0, t) 1537 verifyQLen(r.qsubs, 0, t) 1538 }) 1539 } 1540 }) 1541 } 1542 } 1543 1544 func TestSublistSubjectCollide(t *testing.T) { 1545 require_False(t, SubjectsCollide("foo.*", "foo.*.bar.>")) 1546 require_False(t, SubjectsCollide("foo.*.bar.>", "foo.*")) 1547 require_True(t, SubjectsCollide("foo.*", "foo.foo")) 1548 require_True(t, SubjectsCollide("foo.*", "*.foo")) 1549 require_True(t, SubjectsCollide("foo.bar.>", "*.bar.foo")) 1550 } 1551 1552 func TestSublistAddCacheHitRate(t *testing.T) { 1553 sl1 := NewSublistWithCache() 1554 fooSub := newSub("foo") 1555 sl1.Insert(fooSub) 1556 for i := 0; i < 4; i++ { 1557 sl1.Match("foo") 1558 } 1559 stats1 := sl1.Stats() 1560 require_True(t, stats1.CacheHitRate == 0.75) 1561 1562 sl2 := NewSublistWithCache() 1563 barSub := newSub("bar") 1564 sl2.Insert(barSub) 1565 for i := 0; i < 4; i++ { 1566 sl2.Match("bar") 1567 } 1568 stats2 := sl2.Stats() 1569 require_True(t, stats2.CacheHitRate == 0.75) 1570 1571 ts := &SublistStats{} 1572 ts.add(stats1) 1573 ts.add(stats2) 1574 require_True(t, ts.CacheHitRate == 0.75) 1575 } 1576 1577 // -- Benchmarks Setup -- 1578 1579 var benchSublistSubs []*subscription 1580 var benchSublistSl = NewSublistWithCache() 1581 1582 // https://github.com/golang/go/issues/31859 1583 func TestMain(m *testing.M) { 1584 flag.StringVar(&testDefaultClusterCompression, "cluster_compression", _EMPTY_, "Test with this compression level as the default") 1585 flag.StringVar(&testDefaultLeafNodeCompression, "leafnode_compression", _EMPTY_, "Test with this compression level as the default") 1586 flag.Parse() 1587 initSublist := false 1588 flag.Visit(func(f *flag.Flag) { 1589 if f.Name == "test.bench" { 1590 initSublist = true 1591 } 1592 }) 1593 if initSublist { 1594 benchSublistSubs = make([]*subscription, 0, 256*1024) 1595 toks := []string{"synadia", "nats", "jetstream", "nkeys", "jwt", "deny", "auth", "drain"} 1596 subsInit("", toks) 1597 for i := 0; i < len(benchSublistSubs); i++ { 1598 benchSublistSl.Insert(benchSublistSubs[i]) 1599 } 1600 addWildcards() 1601 } 1602 os.Exit(m.Run()) 1603 } 1604 1605 func subsInit(pre string, toks []string) { 1606 var sub string 1607 for _, t := range toks { 1608 if len(pre) > 0 { 1609 sub = pre + tsep + t 1610 } else { 1611 sub = t 1612 } 1613 benchSublistSubs = append(benchSublistSubs, newSub(sub)) 1614 if len(strings.Split(sub, tsep)) < 5 { 1615 subsInit(sub, toks) 1616 } 1617 } 1618 } 1619 1620 func addWildcards() { 1621 benchSublistSl.Insert(newSub("cloud.>")) 1622 benchSublistSl.Insert(newSub("cloud.nats.component.>")) 1623 benchSublistSl.Insert(newSub("cloud.*.*.nkeys.*")) 1624 } 1625 1626 // -- Benchmarks Setup End -- 1627 1628 func Benchmark______________________SublistInsert(b *testing.B) { 1629 s := NewSublistWithCache() 1630 for i, l := 0, len(benchSublistSubs); i < b.N; i++ { 1631 index := i % l 1632 s.Insert(benchSublistSubs[index]) 1633 } 1634 } 1635 1636 func Benchmark_______________SublistInsertNoCache(b *testing.B) { 1637 s := NewSublistNoCache() 1638 for i, l := 0, len(benchSublistSubs); i < b.N; i++ { 1639 index := i % l 1640 s.Insert(benchSublistSubs[index]) 1641 } 1642 } 1643 1644 func benchSublistTokens(b *testing.B, tokens string) { 1645 for i := 0; i < b.N; i++ { 1646 benchSublistSl.Match(tokens) 1647 } 1648 } 1649 1650 func Benchmark____________SublistMatchSingleToken(b *testing.B) { 1651 benchSublistTokens(b, "synadia") 1652 } 1653 1654 func Benchmark______________SublistMatchTwoTokens(b *testing.B) { 1655 benchSublistTokens(b, "synadia.nats") 1656 } 1657 1658 func Benchmark____________SublistMatchThreeTokens(b *testing.B) { 1659 benchSublistTokens(b, "synadia.nats.jetstream") 1660 } 1661 1662 func Benchmark_____________SublistMatchFourTokens(b *testing.B) { 1663 benchSublistTokens(b, "synadia.nats.jetstream.nkeys") 1664 } 1665 1666 func Benchmark_SublistMatchFourTokensSingleResult(b *testing.B) { 1667 benchSublistTokens(b, "synadia.nats.jetstream.nkeys") 1668 } 1669 1670 func Benchmark_SublistMatchFourTokensMultiResults(b *testing.B) { 1671 benchSublistTokens(b, "cloud.nats.component.router") 1672 } 1673 1674 func Benchmark_______SublistMissOnLastTokenOfFive(b *testing.B) { 1675 benchSublistTokens(b, "synadia.nats.jetstream.nkeys.ZZZZ") 1676 } 1677 1678 func multiRead(b *testing.B, num int) { 1679 var swg, fwg sync.WaitGroup 1680 swg.Add(num) 1681 fwg.Add(num) 1682 s := "synadia.nats.jetstream.nkeys" 1683 for i := 0; i < num; i++ { 1684 go func() { 1685 swg.Done() 1686 swg.Wait() 1687 n := b.N / num 1688 for i := 0; i < n; i++ { 1689 benchSublistSl.Match(s) 1690 } 1691 fwg.Done() 1692 }() 1693 } 1694 swg.Wait() 1695 b.ResetTimer() 1696 fwg.Wait() 1697 } 1698 1699 func Benchmark____________Sublist10XMultipleReads(b *testing.B) { 1700 multiRead(b, 10) 1701 } 1702 1703 func Benchmark___________Sublist100XMultipleReads(b *testing.B) { 1704 multiRead(b, 100) 1705 } 1706 1707 func Benchmark__________Sublist1000XMultipleReads(b *testing.B) { 1708 multiRead(b, 1000) 1709 } 1710 1711 func Benchmark________________SublistMatchLiteral(b *testing.B) { 1712 cachedSubj := "foo.foo.foo.foo.foo.foo.foo.foo.foo.foo" 1713 subjects := []string{ 1714 "foo.foo.foo.foo.foo.foo.foo.foo.foo.foo", 1715 "foo.foo.foo.foo.foo.foo.foo.foo.foo.>", 1716 "foo.foo.foo.foo.foo.foo.foo.foo.>", 1717 "foo.foo.foo.foo.foo.foo.foo.>", 1718 "foo.foo.foo.foo.foo.foo.>", 1719 "foo.foo.foo.foo.foo.>", 1720 "foo.foo.foo.foo.>", 1721 "foo.foo.foo.>", 1722 "foo.foo.>", 1723 "foo.>", 1724 ">", 1725 "foo.foo.foo.foo.foo.foo.foo.foo.foo.*", 1726 "foo.foo.foo.foo.foo.foo.foo.foo.*.*", 1727 "foo.foo.foo.foo.foo.foo.foo.*.*.*", 1728 "foo.foo.foo.foo.foo.foo.*.*.*.*", 1729 "foo.foo.foo.foo.foo.*.*.*.*.*", 1730 "foo.foo.foo.foo.*.*.*.*.*.*", 1731 "foo.foo.foo.*.*.*.*.*.*.*", 1732 "foo.foo.*.*.*.*.*.*.*.*", 1733 "foo.*.*.*.*.*.*.*.*.*", 1734 "*.*.*.*.*.*.*.*.*.*", 1735 } 1736 b.ResetTimer() 1737 for i := 0; i < b.N; i++ { 1738 for _, subject := range subjects { 1739 if !matchLiteral(cachedSubj, subject) { 1740 b.Fatalf("Subject %q no match with %q", cachedSubj, subject) 1741 } 1742 } 1743 } 1744 } 1745 1746 func Benchmark_____SublistMatch10kSubsWithNoCache(b *testing.B) { 1747 var nsubs = 512 1748 s := NewSublistNoCache() 1749 subject := "foo" 1750 for i := 0; i < nsubs; i++ { 1751 s.Insert(newSub(subject)) 1752 } 1753 b.ResetTimer() 1754 for i := 0; i < b.N; i++ { 1755 r := s.Match(subject) 1756 if len(r.psubs) != nsubs { 1757 b.Fatalf("Results len is %d, should be %d", len(r.psubs), nsubs) 1758 } 1759 } 1760 } 1761 1762 func removeTest(b *testing.B, singleSubject, doBatch bool, qgroup string) { 1763 s := NewSublistWithCache() 1764 subject := "foo" 1765 1766 subs := make([]*subscription, 0, b.N) 1767 for i := 0; i < b.N; i++ { 1768 var sub *subscription 1769 if singleSubject { 1770 sub = newQSub(subject, qgroup) 1771 } else { 1772 sub = newQSub(fmt.Sprintf("%s.%d\n", subject, i), qgroup) 1773 } 1774 s.Insert(sub) 1775 subs = append(subs, sub) 1776 } 1777 1778 // Actual test on Remove 1779 b.ResetTimer() 1780 if doBatch { 1781 s.RemoveBatch(subs) 1782 } else { 1783 for _, sub := range subs { 1784 s.Remove(sub) 1785 } 1786 } 1787 } 1788 1789 func Benchmark__________SublistRemove1TokenSingle(b *testing.B) { 1790 removeTest(b, true, false, "") 1791 } 1792 1793 func Benchmark___________SublistRemove1TokenBatch(b *testing.B) { 1794 removeTest(b, true, true, "") 1795 } 1796 1797 func Benchmark_________SublistRemove2TokensSingle(b *testing.B) { 1798 removeTest(b, false, false, "") 1799 } 1800 1801 func Benchmark__________SublistRemove2TokensBatch(b *testing.B) { 1802 removeTest(b, false, true, "") 1803 } 1804 1805 func Benchmark________SublistRemove1TokenQGSingle(b *testing.B) { 1806 removeTest(b, true, false, "bar") 1807 } 1808 1809 func Benchmark_________SublistRemove1TokenQGBatch(b *testing.B) { 1810 removeTest(b, true, true, "bar") 1811 } 1812 1813 func removeMultiTest(b *testing.B, singleSubject, doBatch bool) { 1814 s := NewSublistWithCache() 1815 subject := "foo" 1816 var swg, fwg sync.WaitGroup 1817 swg.Add(b.N) 1818 fwg.Add(b.N) 1819 1820 // We will have b.N go routines each with 1k subscriptions. 1821 sc := 1000 1822 1823 for i := 0; i < b.N; i++ { 1824 go func() { 1825 subs := make([]*subscription, 0, sc) 1826 for n := 0; n < sc; n++ { 1827 var sub *subscription 1828 if singleSubject { 1829 sub = newSub(subject) 1830 } else { 1831 sub = newSub(fmt.Sprintf("%s.%d\n", subject, n)) 1832 } 1833 s.Insert(sub) 1834 subs = append(subs, sub) 1835 } 1836 // Wait to start test 1837 swg.Done() 1838 swg.Wait() 1839 // Actual test on Remove 1840 if doBatch { 1841 s.RemoveBatch(subs) 1842 } else { 1843 for _, sub := range subs { 1844 s.Remove(sub) 1845 } 1846 } 1847 fwg.Done() 1848 }() 1849 } 1850 swg.Wait() 1851 b.ResetTimer() 1852 fwg.Wait() 1853 } 1854 1855 // Check contention rates for remove from multiple Go routines. 1856 // Reason for BatchRemove. 1857 func Benchmark_________SublistRemove1kSingleMulti(b *testing.B) { 1858 removeMultiTest(b, true, false) 1859 } 1860 1861 // Batch version 1862 func Benchmark__________SublistRemove1kBatchMulti(b *testing.B) { 1863 removeMultiTest(b, true, true) 1864 } 1865 1866 func Benchmark__SublistRemove1kSingle2TokensMulti(b *testing.B) { 1867 removeMultiTest(b, false, false) 1868 } 1869 1870 // Batch version 1871 func Benchmark___SublistRemove1kBatch2TokensMulti(b *testing.B) { 1872 removeMultiTest(b, false, true) 1873 } 1874 1875 // Cache contention tests 1876 func cacheContentionTest(b *testing.B, numMatchers, numAdders, numRemovers int) { 1877 var swg, fwg, mwg sync.WaitGroup 1878 total := numMatchers + numAdders + numRemovers 1879 swg.Add(total) 1880 fwg.Add(total) 1881 mwg.Add(numMatchers) 1882 1883 mu := sync.RWMutex{} 1884 subs := make([]*subscription, 0, 8192) 1885 1886 quitCh := make(chan struct{}) 1887 1888 // Set up a new sublist. subjects will be foo.bar.baz.N 1889 s := NewSublistWithCache() 1890 mu.Lock() 1891 for i := 0; i < 10000; i++ { 1892 sub := newSub(fmt.Sprintf("foo.bar.baz.%d", i)) 1893 s.Insert(sub) 1894 subs = append(subs, sub) 1895 } 1896 mu.Unlock() 1897 1898 // Now warm up the cache 1899 for i := 0; i < slCacheMax; i++ { 1900 s.Match(fmt.Sprintf("foo.bar.baz.%d", i)) 1901 } 1902 1903 // Setup go routines. 1904 1905 // Adders 1906 for i := 0; i < numAdders; i++ { 1907 go func() { 1908 swg.Done() 1909 swg.Wait() 1910 for { 1911 select { 1912 case <-quitCh: 1913 fwg.Done() 1914 return 1915 default: 1916 mu.Lock() 1917 next := len(subs) 1918 subj := "foo.bar.baz." + strconv.FormatInt(int64(next), 10) 1919 sub := newSub(subj) 1920 subs = append(subs, sub) 1921 mu.Unlock() 1922 s.Insert(sub) 1923 } 1924 } 1925 }() 1926 } 1927 1928 // Removers 1929 for i := 0; i < numRemovers; i++ { 1930 go func() { 1931 prand := rand.New(rand.NewSource(time.Now().UnixNano())) 1932 swg.Done() 1933 swg.Wait() 1934 for { 1935 select { 1936 case <-quitCh: 1937 fwg.Done() 1938 return 1939 default: 1940 mu.RLock() 1941 lh := len(subs) - 1 1942 index := prand.Intn(lh) 1943 sub := subs[index] 1944 mu.RUnlock() 1945 s.Remove(sub) 1946 } 1947 } 1948 }() 1949 } 1950 1951 // Matchers 1952 for i := 0; i < numMatchers; i++ { 1953 go func() { 1954 id := nuid.New() 1955 swg.Done() 1956 swg.Wait() 1957 1958 // We will miss on purpose to blow the cache. 1959 n := b.N / numMatchers 1960 for i := 0; i < n; i++ { 1961 subj := "foo.bar.baz." + id.Next() 1962 s.Match(subj) 1963 } 1964 mwg.Done() 1965 fwg.Done() 1966 }() 1967 } 1968 1969 swg.Wait() 1970 b.ResetTimer() 1971 mwg.Wait() 1972 b.StopTimer() 1973 close(quitCh) 1974 fwg.Wait() 1975 } 1976 1977 func Benchmark____SublistCacheContention10M10A10R(b *testing.B) { 1978 cacheContentionTest(b, 10, 10, 10) 1979 } 1980 1981 func Benchmark_SublistCacheContention100M100A100R(b *testing.B) { 1982 cacheContentionTest(b, 100, 100, 100) 1983 } 1984 1985 func Benchmark____SublistCacheContention1kM1kA1kR(b *testing.B) { 1986 cacheContentionTest(b, 1024, 1024, 1024) 1987 } 1988 1989 func Benchmark_SublistCacheContention10kM10kA10kR(b *testing.B) { 1990 cacheContentionTest(b, 10*1024, 10*1024, 10*1024) 1991 } 1992 1993 func Benchmark______________IsValidLiteralSubject(b *testing.B) { 1994 for i := 0; i < b.N; i++ { 1995 IsValidLiteralSubject("foo.bar.baz.22") 1996 } 1997 } 1998 1999 func Benchmark___________________subjectIsLiteral(b *testing.B) { 2000 for i := 0; i < b.N; i++ { 2001 subjectIsLiteral("foo.bar.baz.22") 2002 } 2003 }