github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/jetstream_super_cluster_test.go (about) 1 // Copyright 2020-2022 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 //go:build !skip_js_tests && !skip_js_cluster_tests && !skip_js_cluster_tests_2 && !skip_js_super_cluster_tests 15 // +build !skip_js_tests,!skip_js_cluster_tests,!skip_js_cluster_tests_2,!skip_js_super_cluster_tests 16 17 package server 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math/rand" 24 "net/http" 25 "net/http/httptest" 26 "reflect" 27 "strings" 28 "sync" 29 "sync/atomic" 30 "testing" 31 "time" 32 33 "github.com/nats-io/jwt/v2" 34 "github.com/nats-io/nats.go" 35 "github.com/nats-io/nkeys" 36 ) 37 38 func TestJetStreamSuperClusterMetaPlacement(t *testing.T) { 39 sc := createJetStreamSuperCluster(t, 3, 3) 40 defer sc.shutdown() 41 42 // We want to influence where the meta leader will place itself when we ask the 43 // current leader to stepdown. 44 ml := sc.leader() 45 cn := ml.ClusterName() 46 var pcn string 47 for _, c := range sc.clusters { 48 if c.name != cn { 49 pcn = c.name 50 break 51 } 52 } 53 54 // Client based API 55 s := sc.randomServer() 56 nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 57 if err != nil { 58 t.Fatalf("Failed to create system client: %v", err) 59 } 60 defer nc.Close() 61 62 stepdown := func(cn string) *JSApiLeaderStepDownResponse { 63 req := &JSApiLeaderStepdownRequest{Placement: &Placement{Cluster: cn}} 64 jreq, err := json.Marshal(req) 65 if err != nil { 66 t.Fatalf("Unexpected error: %v", err) 67 } 68 69 resp, err := nc.Request(JSApiLeaderStepDown, jreq, time.Second) 70 if err != nil { 71 t.Fatalf("Error on stepdown request: %v", err) 72 } 73 var sdr JSApiLeaderStepDownResponse 74 if err := json.Unmarshal(resp.Data, &sdr); err != nil { 75 t.Fatalf("Unexpected error: %v", err) 76 } 77 return &sdr 78 } 79 80 // Make sure we get correct errors for tags and bad or unavailable cluster placement. 81 sdr := stepdown("C22") 82 if sdr.Error == nil || !strings.Contains(sdr.Error.Description, "no replacement peer connected") { 83 t.Fatalf("Got incorrect error result: %+v", sdr.Error) 84 } 85 // Should work. 86 sdr = stepdown(pcn) 87 if sdr.Error != nil { 88 t.Fatalf("Got an error on stepdown: %+v", sdr.Error) 89 } 90 91 sc.waitOnLeader() 92 ml = sc.leader() 93 cn = ml.ClusterName() 94 95 if cn != pcn { 96 t.Fatalf("Expected new metaleader to be in cluster %q, got %q", pcn, cn) 97 } 98 } 99 100 func TestJetStreamSuperClusterUniquePlacementTag(t *testing.T) { 101 tmlp := ` 102 listen: 127.0.0.1:-1 103 server_name: %s 104 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s', unique_tag: az} 105 leaf {listen: 127.0.0.1:-1} 106 cluster { 107 name: %s 108 listen: 127.0.0.1:%d 109 routes = [%s] 110 } 111 # For access to system account. 112 accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } } 113 ` 114 s := createJetStreamSuperClusterWithTemplateAndModHook(t, tmlp, 5, 2, 115 func(serverName, clustername, storeDir, conf string) string { 116 azTag := map[string]string{ 117 "C1-S1": "az:same", 118 "C1-S2": "az:same", 119 "C1-S3": "az:same", 120 "C1-S4": "az:same", 121 "C1-S5": "az:same", 122 "C2-S1": "az:1", 123 "C2-S2": "az:2", 124 "C2-S3": "az:1", 125 "C2-S4": "az:2", 126 "C2-S5": "az:1", 127 } 128 return conf + fmt.Sprintf("\nserver_tags: [cloud:%s-tag, %s]\n", clustername, azTag[serverName]) 129 }, nil) 130 defer s.shutdown() 131 132 inDifferentAz := func(ci *nats.ClusterInfo) (bool, error) { 133 t.Helper() 134 if len(ci.Replicas) == 0 { 135 return true, nil 136 } 137 // if R2 (has replica, this setup does not support R3), test if the server in a cluster picked the same az, 138 // as determined by modulo2 of server number which aligns with az 139 dummy := 0 140 srvnum1 := 0 141 srvnum2 := 0 142 if n, _ := fmt.Sscanf(ci.Leader, "C%d-S%d", &dummy, &srvnum1); n != 2 { 143 return false, fmt.Errorf("couldn't parse leader") 144 } 145 if n, _ := fmt.Sscanf(ci.Replicas[0].Name, "C%d-S%d", &dummy, &srvnum2); n != 2 { 146 return false, fmt.Errorf("couldn't parse replica") 147 } 148 return srvnum1%2 != srvnum2%2, nil 149 } 150 151 nc := natsConnect(t, s.randomServer().ClientURL()) 152 defer nc.Close() 153 154 js, err := nc.JetStream() 155 require_NoError(t, err) 156 157 for i, test := range []struct { 158 placement *nats.Placement 159 replicas int 160 fail bool 161 cluster string 162 }{ 163 // these pass because replica count is 1 164 {&nats.Placement{Tags: []string{"az:same"}}, 1, false, "C1"}, 165 {&nats.Placement{Tags: []string{"cloud:C1-tag", "az:same"}}, 1, false, "C1"}, 166 {&nats.Placement{Tags: []string{"cloud:C1-tag"}}, 1, false, "C1"}, 167 // pass because az is set, which disables the filter 168 {&nats.Placement{Tags: []string{"az:same"}}, 2, false, "C1"}, 169 {&nats.Placement{Tags: []string{"cloud:C1-tag", "az:same"}}, 2, false, "C1"}, 170 // fails because this cluster only has the same az 171 {&nats.Placement{Tags: []string{"cloud:C1-tag"}}, 2, true, ""}, 172 // fails because no 3 unique tags exist 173 {&nats.Placement{Tags: []string{"cloud:C2-tag"}}, 3, true, ""}, 174 {nil, 3, true, ""}, 175 // pass because replica count is low enough 176 {nil, 2, false, "C2"}, 177 {&nats.Placement{Tags: []string{"cloud:C2-tag"}}, 2, false, "C2"}, 178 // pass because az is provided 179 {&nats.Placement{Tags: []string{"az:1"}}, 3, false, "C2"}, 180 {&nats.Placement{Tags: []string{"az:2"}}, 2, false, "C2"}, 181 } { 182 name := fmt.Sprintf("test-%d", i) 183 t.Run(name, func(t *testing.T) { 184 si, err := js.AddStream(&nats.StreamConfig{Name: name, Replicas: test.replicas, Placement: test.placement}) 185 if test.fail { 186 require_Error(t, err) 187 require_Contains(t, err.Error(), "no suitable peers for placement", "server tag not unique") 188 return 189 } 190 require_NoError(t, err) 191 if test.cluster != _EMPTY_ { 192 require_Equal(t, si.Cluster.Name, test.cluster) 193 } 194 // skip placement test if tags call for a particular az 195 if test.placement != nil && len(test.placement.Tags) > 0 { 196 for _, tag := range test.placement.Tags { 197 if strings.HasPrefix(tag, "az:") { 198 return 199 } 200 } 201 } 202 diff, err := inDifferentAz(si.Cluster) 203 require_NoError(t, err) 204 require_True(t, diff) 205 }) 206 } 207 208 t.Run("scale-up-test", func(t *testing.T) { 209 // create enough streams so we hit it eventually 210 for i := 0; i < 10; i++ { 211 cfg := &nats.StreamConfig{Name: fmt.Sprintf("scale-up-%d", i), Replicas: 1, 212 Placement: &nats.Placement{Tags: []string{"cloud:C2-tag"}}} 213 si, err := js.AddStream(cfg) 214 require_NoError(t, err) 215 require_Equal(t, si.Cluster.Name, "C2") 216 cfg.Replicas = 2 217 si, err = js.UpdateStream(cfg) 218 require_NoError(t, err) 219 require_Equal(t, si.Cluster.Name, "C2") 220 checkFor(t, 10, 250*time.Millisecond, func() error { 221 if si, err := js.StreamInfo(cfg.Name); err != nil { 222 return err 223 } else if diff, err := inDifferentAz(si.Cluster); err != nil { 224 return err 225 } else if !diff { 226 return fmt.Errorf("not in different AZ") 227 } 228 return nil 229 }) 230 } 231 }) 232 } 233 234 func TestJetStreamSuperClusterBasics(t *testing.T) { 235 sc := createJetStreamSuperCluster(t, 3, 3) 236 defer sc.shutdown() 237 238 // Client based API 239 s := sc.randomServer() 240 nc, js := jsClientConnect(t, s) 241 defer nc.Close() 242 243 _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3}) 244 if err != nil { 245 t.Fatalf("Unexpected error: %v", err) 246 } 247 248 // Send in 10 messages. 249 msg, toSend := []byte("Hello JS Clustering"), 10 250 for i := 0; i < toSend; i++ { 251 if _, err = js.Publish("TEST", msg); err != nil { 252 t.Fatalf("Unexpected publish error: %v", err) 253 } 254 } 255 // Now grab info for this stream. 256 si, err := js.StreamInfo("TEST") 257 if err != nil { 258 t.Fatalf("Unexpected error: %v", err) 259 } 260 if si == nil || si.Config.Name != "TEST" { 261 t.Fatalf("StreamInfo is not correct %+v", si) 262 } 263 // Check active state as well, shows that the owner answered. 264 if si.State.Msgs != uint64(toSend) { 265 t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State) 266 } 267 // Check request origin placement. 268 if si.Cluster.Name != s.ClusterName() { 269 t.Fatalf("Expected stream to be placed in %q, but got %q", s.ClusterName(), si.Cluster.Name) 270 } 271 272 // Check consumers. 273 sub, err := js.SubscribeSync("TEST") 274 if err != nil { 275 t.Fatalf("Unexpected error: %v", err) 276 } 277 checkSubsPending(t, sub, toSend) 278 ci, err := sub.ConsumerInfo() 279 if err != nil { 280 t.Fatalf("Unexpected error: %v", err) 281 } 282 if ci.Delivered.Consumer != uint64(toSend) || ci.NumAckPending != toSend { 283 t.Fatalf("ConsumerInfo is not correct: %+v", ci) 284 } 285 286 // Now check we can place a stream. 287 pcn := "C3" 288 scResp, err := js.AddStream(&nats.StreamConfig{ 289 Name: "TEST2", 290 Placement: &nats.Placement{Cluster: pcn}, 291 }) 292 if err != nil { 293 t.Fatalf("Unexpected error: %v", err) 294 } 295 296 if scResp.Cluster.Name != pcn { 297 t.Fatalf("Expected the stream to be placed in %q, got %q", pcn, scResp.Cluster.Name) 298 } 299 } 300 301 // Test that consumer interest across gateways and superclusters is properly identitifed in a remote cluster. 302 func TestJetStreamSuperClusterCrossClusterConsumerInterest(t *testing.T) { 303 sc := createJetStreamSuperCluster(t, 3, 3) 304 defer sc.shutdown() 305 306 // Since we need all of the peers accounted for to add the stream wait for all to be present. 307 sc.waitOnPeerCount(9) 308 309 // Client based API - Connect to Cluster C1. Stream and consumer will live in C2. 310 s := sc.clusterForName("C1").randomServer() 311 nc, js := jsClientConnect(t, s) 312 defer nc.Close() 313 314 pcn := "C2" 315 _, err := js.AddStream(&nats.StreamConfig{Name: "foo", Replicas: 3, Placement: &nats.Placement{Cluster: pcn}}) 316 if err != nil { 317 t.Fatalf("Unexpected error: %v", err) 318 } 319 320 // Pull based first. 321 sub, err := js.PullSubscribe("foo", "dlc") 322 if err != nil { 323 t.Fatalf("Unexpected error: %v", err) 324 } 325 326 // Send a message. 327 if _, err = js.Publish("foo", []byte("CCI")); err != nil { 328 t.Fatalf("Unexpected publish error: %v", err) 329 } 330 331 fetchMsgs(t, sub, 1, 5*time.Second) 332 333 // Now check push based delivery. 334 sub, err = js.SubscribeSync("foo", nats.Durable("rip")) 335 if err != nil { 336 t.Fatalf("Unexpected error: %v", err) 337 } 338 checkSubsPending(t, sub, 1) 339 340 // Send another message. 341 if _, err = js.Publish("foo", []byte("CCI")); err != nil { 342 t.Fatalf("Unexpected publish error: %v", err) 343 } 344 checkSubsPending(t, sub, 2) 345 } 346 347 func TestJetStreamSuperClusterPeerReassign(t *testing.T) { 348 sc := createJetStreamSuperCluster(t, 3, 3) 349 defer sc.shutdown() 350 351 // Client based API 352 s := sc.randomServer() 353 nc, js := jsClientConnect(t, s) 354 defer nc.Close() 355 356 pcn := "C2" 357 358 // Create a stream in C2 that sources TEST 359 _, err := js.AddStream(&nats.StreamConfig{ 360 Name: "TEST", 361 Placement: &nats.Placement{Cluster: pcn}, 362 Replicas: 3, 363 }) 364 if err != nil { 365 t.Fatalf("Unexpected error: %v", err) 366 } 367 368 // Send in 10 messages. 369 msg, toSend := []byte("Hello JS Clustering"), 10 370 for i := 0; i < toSend; i++ { 371 if _, err = js.Publish("TEST", msg); err != nil { 372 t.Fatalf("Unexpected publish error: %v", err) 373 } 374 } 375 // Now grab info for this stream. 376 si, err := js.StreamInfo("TEST") 377 if err != nil { 378 t.Fatalf("Unexpected error: %v", err) 379 } 380 if si == nil || si.Config.Name != "TEST" { 381 t.Fatalf("StreamInfo is not correct %+v", si) 382 } 383 // Check active state as well, shows that the owner answered. 384 if si.State.Msgs != uint64(toSend) { 385 t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State) 386 } 387 // Check request origin placement. 388 if si.Cluster.Name != pcn { 389 t.Fatalf("Expected stream to be placed in %q, but got %q", s.ClusterName(), si.Cluster.Name) 390 } 391 392 // Now remove a peer that is assigned to the stream. 393 rc := sc.clusterForName(pcn) 394 rs := rc.randomNonStreamLeader("$G", "TEST") 395 rc.removeJetStream(rs) 396 397 // Check the stream info is eventually correct. 398 checkFor(t, 2*time.Second, 50*time.Millisecond, func() error { 399 si, err := js.StreamInfo("TEST") 400 if err != nil { 401 return fmt.Errorf("Could not fetch stream info: %v", err) 402 } 403 if len(si.Cluster.Replicas) != 2 { 404 return fmt.Errorf("Expected 2 replicas, got %d", len(si.Cluster.Replicas)) 405 } 406 for _, peer := range si.Cluster.Replicas { 407 if !peer.Current { 408 return fmt.Errorf("Expected replica to be current: %+v", peer) 409 } 410 if !strings.HasPrefix(peer.Name, pcn) { 411 t.Fatalf("Stream peer reassigned to wrong cluster: %q", peer.Name) 412 } 413 } 414 return nil 415 }) 416 } 417 418 func TestJetStreamSuperClusterInterestOnlyMode(t *testing.T) { 419 GatewayDoNotForceInterestOnlyMode(true) 420 defer GatewayDoNotForceInterestOnlyMode(false) 421 422 template := ` 423 listen: 127.0.0.1:-1 424 server_name: %s 425 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 426 accounts { 427 one { 428 jetstream: enabled 429 users [{user: one, password: password}] 430 } 431 two { 432 %s 433 users [{user: two, password: password}] 434 } 435 } 436 cluster { 437 listen: 127.0.0.1:%d 438 name: %s 439 routes = ["nats://127.0.0.1:%d"] 440 } 441 gateway { 442 name: %s 443 listen: 127.0.0.1:%d 444 gateways = [{name: %s, urls: ["nats://127.0.0.1:%d"]}] 445 } 446 ` 447 storeDir1 := t.TempDir() 448 conf1 := createConfFile(t, []byte(fmt.Sprintf(template, 449 "S1", storeDir1, "", 23222, "A", 23222, "A", 11222, "B", 11223))) 450 s1, o1 := RunServerWithConfig(conf1) 451 defer s1.Shutdown() 452 453 storeDir2 := t.TempDir() 454 conf2 := createConfFile(t, []byte(fmt.Sprintf(template, 455 "S2", storeDir2, "", 23223, "B", 23223, "B", 11223, "A", 11222))) 456 s2, o2 := RunServerWithConfig(conf2) 457 defer s2.Shutdown() 458 459 waitForInboundGateways(t, s1, 1, 2*time.Second) 460 waitForInboundGateways(t, s2, 1, 2*time.Second) 461 waitForOutboundGateways(t, s1, 1, 2*time.Second) 462 waitForOutboundGateways(t, s2, 1, 2*time.Second) 463 464 nc1 := natsConnect(t, fmt.Sprintf("nats://two:password@127.0.0.1:%d", o1.Port)) 465 defer nc1.Close() 466 nc1.Publish("foo", []byte("some message")) 467 nc1.Flush() 468 469 nc2 := natsConnect(t, fmt.Sprintf("nats://two:password@127.0.0.1:%d", o2.Port)) 470 defer nc2.Close() 471 nc2.Publish("bar", []byte("some message")) 472 nc2.Flush() 473 474 checkMode := func(accName string, expectedMode GatewayInterestMode) { 475 t.Helper() 476 checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { 477 servers := []*Server{s1, s2} 478 for _, s := range servers { 479 var gws []*client 480 s.getInboundGatewayConnections(&gws) 481 for _, gw := range gws { 482 var mode GatewayInterestMode 483 gw.mu.Lock() 484 ie := gw.gw.insim[accName] 485 if ie != nil { 486 mode = ie.mode 487 } 488 gw.mu.Unlock() 489 if ie == nil { 490 return fmt.Errorf("Account %q not in map", accName) 491 } 492 if mode != expectedMode { 493 return fmt.Errorf("Expected account %q mode to be %v, got: %v", accName, expectedMode, mode) 494 } 495 } 496 } 497 return nil 498 }) 499 } 500 501 checkMode("one", InterestOnly) 502 checkMode("two", Optimistic) 503 504 // Now change account "two" to enable JS 505 changeCurrentConfigContentWithNewContent(t, conf1, []byte(fmt.Sprintf(template, 506 "S1", storeDir1, "jetstream: enabled", 23222, "A", 23222, "A", 11222, "B", 11223))) 507 changeCurrentConfigContentWithNewContent(t, conf2, []byte(fmt.Sprintf(template, 508 "S2", storeDir2, "jetstream: enabled", 23223, "B", 23223, "B", 11223, "A", 11222))) 509 510 if err := s1.Reload(); err != nil { 511 t.Fatalf("Error on s1 reload: %v", err) 512 } 513 if err := s2.Reload(); err != nil { 514 t.Fatalf("Error on s2 reload: %v", err) 515 } 516 517 checkMode("one", InterestOnly) 518 checkMode("two", InterestOnly) 519 } 520 521 func TestJetStreamSuperClusterConnectionCount(t *testing.T) { 522 sc := createJetStreamSuperClusterWithTemplate(t, jsClusterAccountsTempl, 3, 2) 523 defer sc.shutdown() 524 525 sysNc := natsConnect(t, sc.randomServer().ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 526 defer sysNc.Close() 527 _, err := sysNc.Request(fmt.Sprintf(accDirectReqSubj, "ONE", "CONNS"), nil, 100*time.Millisecond) 528 // this is a timeout as the server only responds when it has connections.... 529 // not convinced this should be that way, but also not the issue to investigate. 530 require_True(t, err == nats.ErrTimeout) 531 532 for i := 1; i <= 2; i++ { 533 func() { 534 nc := natsConnect(t, sc.clusterForName(fmt.Sprintf("C%d", i)).randomServer().ClientURL()) 535 defer nc.Close() 536 js, err := nc.JetStream() 537 require_NoError(t, err) 538 name := fmt.Sprintf("foo%d", 1) 539 _, err = js.AddStream(&nats.StreamConfig{ 540 Name: name, 541 Subjects: []string{name}, 542 Replicas: 3}) 543 require_NoError(t, err) 544 }() 545 } 546 func() { 547 nc := natsConnect(t, sc.clusterForName("C1").randomServer().ClientURL()) 548 defer nc.Close() 549 js, err := nc.JetStream() 550 require_NoError(t, err) 551 _, err = js.AddStream(&nats.StreamConfig{ 552 Name: "src", 553 Sources: []*nats.StreamSource{{Name: "foo1"}, {Name: "foo2"}}, 554 Replicas: 3}) 555 require_NoError(t, err) 556 }() 557 func() { 558 nc := natsConnect(t, sc.clusterForName("C2").randomServer().ClientURL()) 559 defer nc.Close() 560 js, err := nc.JetStream() 561 require_NoError(t, err) 562 _, err = js.AddStream(&nats.StreamConfig{ 563 Name: "mir", 564 Mirror: &nats.StreamSource{Name: "foo2"}, 565 Replicas: 3}) 566 require_NoError(t, err) 567 }() 568 569 // There should be no active NATS CLIENT connections, but we still need 570 // to wait a little bit... 571 checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { 572 _, err := sysNc.Request(fmt.Sprintf(accDirectReqSubj, "ONE", "CONNS"), nil, 100*time.Millisecond) 573 if err != nats.ErrTimeout { 574 return fmt.Errorf("Expected timeout, got %v", err) 575 } 576 return nil 577 }) 578 sysNc.Close() 579 580 s := sc.randomServer() 581 checkFor(t, 5*time.Second, 100*time.Millisecond, func() error { 582 acc, err := s.lookupAccount("ONE") 583 if err != nil { 584 t.Fatalf("Could not look up account: %v", err) 585 } 586 if n := acc.NumConnections(); n != 0 { 587 return fmt.Errorf("Expected no connections, got %d", n) 588 } 589 return nil 590 }) 591 } 592 593 func TestJetStreamSuperClusterConsumersBrokenGateways(t *testing.T) { 594 sc := createJetStreamSuperCluster(t, 1, 2) 595 defer sc.shutdown() 596 597 // Client based API 598 s := sc.clusterForName("C1").randomServer() 599 nc, js := jsClientConnect(t, s) 600 defer nc.Close() 601 602 // This will be in C1. 603 _, err := js.AddStream(&nats.StreamConfig{Name: "TEST"}) 604 if err != nil { 605 t.Fatalf("Unexpected error: %v", err) 606 } 607 608 // Create a stream in C2 that sources TEST 609 _, err = js.AddStream(&nats.StreamConfig{ 610 Name: "S", 611 Placement: &nats.Placement{Cluster: "C2"}, 612 Sources: []*nats.StreamSource{{Name: "TEST"}}, 613 }) 614 if err != nil { 615 t.Fatalf("Unexpected error: %v", err) 616 } 617 618 // Wait for direct consumer to get registered and detect interest across GW. 619 time.Sleep(time.Second) 620 621 // Send 100 msgs over 100ms in separate Go routine. 622 msg, toSend, done := []byte("Hello"), 100, make(chan bool) 623 go func() { 624 // Send in 10 messages. 625 for i := 0; i < toSend; i++ { 626 if _, err = js.Publish("TEST", msg); err != nil { 627 t.Errorf("Unexpected publish error: %v", err) 628 } 629 time.Sleep(500 * time.Microsecond) 630 } 631 done <- true 632 }() 633 634 breakGW := func() { 635 s.gateway.Lock() 636 gw := s.gateway.out["C2"] 637 s.gateway.Unlock() 638 if gw != nil { 639 gw.closeConnection(ClientClosed) 640 } 641 } 642 643 // Wait til about half way through. 644 time.Sleep(20 * time.Millisecond) 645 // Now break GW connection. 646 breakGW() 647 648 // Wait for GW to reform. 649 for _, c := range sc.clusters { 650 for _, s := range c.servers { 651 waitForOutboundGateways(t, s, 1, 2*time.Second) 652 } 653 } 654 655 select { 656 case <-done: 657 case <-time.After(2 * time.Second): 658 t.Fatalf("Did not complete sending first batch of messages") 659 } 660 661 // Make sure we can deal with data loss at the end. 662 checkFor(t, 20*time.Second, 250*time.Millisecond, func() error { 663 si, err := js.StreamInfo("S") 664 if err != nil { 665 t.Fatalf("Unexpected error: %v", err) 666 } 667 if si.State.Msgs != 100 { 668 return fmt.Errorf("Expected to have %d messages, got %d", 100, si.State.Msgs) 669 } 670 return nil 671 }) 672 673 // Now send 100 more. Will aos break here in the middle. 674 for i := 0; i < toSend; i++ { 675 if _, err = js.Publish("TEST", msg); err != nil { 676 t.Fatalf("Unexpected publish error: %v", err) 677 } 678 if i == 50 { 679 breakGW() 680 } 681 } 682 683 // Wait for GW to reform. 684 for _, c := range sc.clusters { 685 for _, s := range c.servers { 686 waitForOutboundGateways(t, s, 1, 2*time.Second) 687 } 688 } 689 690 si, err := js.StreamInfo("TEST") 691 if err != nil { 692 t.Fatalf("Unexpected error: %v", err) 693 } 694 if si.State.Msgs != 200 { 695 t.Fatalf("Expected to have %d messages, got %d", 200, si.State.Msgs) 696 } 697 698 checkFor(t, 10*time.Second, 250*time.Millisecond, func() error { 699 si, err := js.StreamInfo("S") 700 if err != nil { 701 return fmt.Errorf("Unexpected error: %v", err) 702 } 703 if si.State.Msgs != 200 { 704 return fmt.Errorf("Expected to have %d messages, got %d", 200, si.State.Msgs) 705 } 706 return nil 707 }) 708 } 709 710 func TestJetStreamSuperClusterLeafNodesWithSharedSystemAccountAndSameDomain(t *testing.T) { 711 sc := createJetStreamSuperCluster(t, 3, 2) 712 defer sc.shutdown() 713 714 lnc := sc.createLeafNodes("LNC", 2) 715 defer lnc.shutdown() 716 717 // We want to make sure there is only one leader and its always in the supercluster. 718 sc.waitOnLeader() 719 720 if ml := lnc.leader(); ml != nil { 721 t.Fatalf("Detected a meta-leader in the leafnode cluster: %s", ml) 722 } 723 724 // leafnodes should have been added into the overall peer count. 725 sc.waitOnPeerCount(8) 726 727 // Check here that we auto detect sharing system account as well and auto place the correct 728 // deny imports and exports. 729 ls := lnc.randomServer() 730 if ls == nil { 731 t.Fatalf("Expected a leafnode server, got none") 732 } 733 gacc := ls.globalAccount().GetName() 734 735 ls.mu.Lock() 736 var hasDE, hasDI bool 737 for _, ln := range ls.leafs { 738 ln.mu.Lock() 739 if ln.leaf.remote.RemoteLeafOpts.LocalAccount == gacc { 740 re := ln.perms.pub.deny.Match(jsAllAPI) 741 hasDE = len(re.psubs)+len(re.qsubs) > 0 742 rs := ln.perms.sub.deny.Match(jsAllAPI) 743 hasDI = len(rs.psubs)+len(rs.qsubs) > 0 744 } 745 ln.mu.Unlock() 746 } 747 ls.mu.Unlock() 748 749 if !hasDE { 750 t.Fatalf("No deny export on global account") 751 } 752 if !hasDI { 753 t.Fatalf("No deny import on global account") 754 } 755 756 // Make a stream by connecting to the leafnode cluster. Make sure placement is correct. 757 // Client based API 758 nc, js := jsClientConnect(t, lnc.randomServer()) 759 defer nc.Close() 760 761 si, err := js.AddStream(&nats.StreamConfig{ 762 Name: "TEST", 763 Subjects: []string{"foo", "bar"}, 764 Replicas: 2, 765 }) 766 if err != nil { 767 t.Fatalf("Unexpected error: %v", err) 768 } 769 if si.Cluster.Name != "LNC" { 770 t.Fatalf("Expected default placement to be %q, got %q", "LNC", si.Cluster.Name) 771 } 772 773 // Now make sure placement also works if we want to place in a cluster in the supercluster. 774 pcn := "C2" 775 si, err = js.AddStream(&nats.StreamConfig{ 776 Name: "TEST2", 777 Subjects: []string{"baz"}, 778 Replicas: 2, 779 Placement: &nats.Placement{Cluster: pcn}, 780 }) 781 if err != nil { 782 t.Fatalf("Unexpected error: %v", err) 783 } 784 if si.Cluster.Name != pcn { 785 t.Fatalf("Expected default placement to be %q, got %q", pcn, si.Cluster.Name) 786 } 787 } 788 789 func TestJetStreamSuperClusterLeafNodesWithSharedSystemAccountAndDifferentDomain(t *testing.T) { 790 sc := createJetStreamSuperCluster(t, 3, 2) 791 defer sc.shutdown() 792 793 lnc := sc.createLeafNodesWithDomain("LNC", 2, "LEAFDOMAIN") 794 defer lnc.shutdown() 795 796 // We want to make sure there is only one leader and its always in the supercluster. 797 sc.waitOnLeader() 798 lnc.waitOnLeader() 799 800 // even though system account is shared, because domains differ, 801 sc.waitOnPeerCount(6) 802 lnc.waitOnPeerCount(2) 803 804 // Check here that we auto detect sharing system account as well and auto place the correct 805 // deny imports and exports. 806 ls := lnc.randomServer() 807 if ls == nil { 808 t.Fatalf("Expected a leafnode server, got none") 809 } 810 gacc := ls.globalAccount().GetName() 811 812 ls.mu.Lock() 813 var hasDE, hasDI bool 814 for _, ln := range ls.leafs { 815 ln.mu.Lock() 816 if ln.leaf.remote.RemoteLeafOpts.LocalAccount == gacc { 817 re := ln.perms.pub.deny.Match(jsAllAPI) 818 hasDE = len(re.psubs)+len(re.qsubs) > 0 819 rs := ln.perms.sub.deny.Match(jsAllAPI) 820 hasDI = len(rs.psubs)+len(rs.qsubs) > 0 821 } 822 ln.mu.Unlock() 823 } 824 ls.mu.Unlock() 825 826 if !hasDE { 827 t.Fatalf("No deny export on global account") 828 } 829 if !hasDI { 830 t.Fatalf("No deny import on global account") 831 } 832 833 // Make a stream by connecting to the leafnode cluster. Make sure placement is correct. 834 // Client based API 835 nc, js := jsClientConnect(t, lnc.randomServer()) 836 defer nc.Close() 837 838 si, err := js.AddStream(&nats.StreamConfig{ 839 Name: "TEST", 840 Subjects: []string{"foo", "bar"}, 841 Replicas: 2, 842 }) 843 if err != nil { 844 t.Fatalf("Unexpected error: %v", err) 845 } 846 if si.Cluster.Name != "LNC" { 847 t.Fatalf("Expected default placement to be %q, got %q", "LNC", si.Cluster.Name) 848 } 849 850 // Now make sure placement does not works for cluster in different domain 851 pcn := "C2" 852 _, err = js.AddStream(&nats.StreamConfig{ 853 Name: "TEST2", 854 Subjects: []string{"baz"}, 855 Replicas: 2, 856 Placement: &nats.Placement{Cluster: pcn}, 857 }) 858 if err == nil || !strings.Contains(err.Error(), "no suitable peers for placement") { 859 t.Fatalf("Expected no suitable peers for placement, got: %v", err) 860 } 861 } 862 863 func TestJetStreamSuperClusterSingleLeafNodeWithSharedSystemAccount(t *testing.T) { 864 sc := createJetStreamSuperCluster(t, 3, 2) 865 defer sc.shutdown() 866 867 ln := sc.createSingleLeafNode(true) 868 defer ln.Shutdown() 869 870 // We want to make sure there is only one leader and its always in the supercluster. 871 sc.waitOnLeader() 872 873 // leafnodes should have been added into the overall peer count. 874 sc.waitOnPeerCount(7) 875 876 // Now make sure we can place a stream in the leaf node. 877 // First connect to the leafnode server itself. 878 nc, js := jsClientConnect(t, ln) 879 defer nc.Close() 880 881 si, err := js.AddStream(&nats.StreamConfig{ 882 Name: "TEST1", 883 Subjects: []string{"foo"}, 884 }) 885 if err != nil { 886 t.Fatalf("Unexpected error: %v", err) 887 } 888 if si.Cluster.Name != "LNS" { 889 t.Fatalf("Expected to be placed in leafnode with %q as cluster name, got %q", "LNS", si.Cluster.Name) 890 } 891 // Now check we can place on here as well but connect to the hub. 892 nc, js = jsClientConnect(t, sc.randomServer()) 893 defer nc.Close() 894 895 si, err = js.AddStream(&nats.StreamConfig{ 896 Name: "TEST2", 897 Subjects: []string{"bar"}, 898 Placement: &nats.Placement{Cluster: "LNS"}, 899 }) 900 if err != nil { 901 t.Fatalf("Unexpected error: %v", err) 902 } 903 if si.Cluster.Name != "LNS" { 904 t.Fatalf("Expected to be placed in leafnode with %q as cluster name, got %q", "LNS", si.Cluster.Name) 905 } 906 } 907 908 // Issue reported with superclusters and leafnodes where first few get next requests for pull subscribers 909 // have the wrong subject. 910 func TestJetStreamSuperClusterGetNextRewrite(t *testing.T) { 911 sc := createJetStreamSuperClusterWithTemplate(t, jsClusterAccountsTempl, 2, 2) 912 defer sc.shutdown() 913 914 // Will connect the leafnode to cluster C1. We will then connect the "client" to cluster C2 to cross gateways. 915 ln := sc.clusterForName("C1").createSingleLeafNodeNoSystemAccountAndEnablesJetStreamWithDomain("C", "nojs") 916 defer ln.Shutdown() 917 918 c2 := sc.clusterForName("C2") 919 nc, js := jsClientConnectEx(t, c2.randomServer(), []nats.JSOpt{nats.Domain("C")}, nats.UserInfo("nojs", "p")) 920 defer nc.Close() 921 922 // Create a stream and add messages. 923 if _, err := js.AddStream(&nats.StreamConfig{Name: "foo"}); err != nil { 924 t.Fatalf("Unexpected error: %v", err) 925 } 926 for i := 0; i < 10; i++ { 927 if _, err := js.Publish("foo", []byte("ok")); err != nil { 928 t.Fatalf("Unexpected publish error: %v", err) 929 } 930 } 931 932 // Pull messages and make sure subject rewrite works. 933 sub, err := js.PullSubscribe("foo", "dlc") 934 if err != nil { 935 t.Fatalf("Unexpected error: %v", err) 936 } 937 938 for _, m := range fetchMsgs(t, sub, 5, time.Second) { 939 if m.Subject != "foo" { 940 t.Fatalf("Expected %q as subject but got %q", "foo", m.Subject) 941 } 942 } 943 } 944 945 func TestJetStreamSuperClusterEphemeralCleanup(t *testing.T) { 946 sc := createJetStreamSuperCluster(t, 3, 2) 947 defer sc.shutdown() 948 949 // Create a stream in cluster 0 950 s := sc.clusters[0].randomServer() 951 nc, js := jsClientConnect(t, s) 952 defer nc.Close() 953 954 for _, test := range []struct { 955 name string 956 sourceInCluster int 957 streamName string 958 sourceName string 959 }{ 960 {"local", 0, "TEST1", "S1"}, 961 {"remote", 1, "TEST2", "S2"}, 962 } { 963 t.Run(test.name, func(t *testing.T) { 964 if _, err := js.AddStream(&nats.StreamConfig{Name: test.streamName, Replicas: 3}); err != nil { 965 t.Fatalf("Error adding %q stream: %v", test.streamName, err) 966 } 967 if _, err := js.Publish(test.streamName, []byte("hello")); err != nil { 968 t.Fatalf("Unexpected publish error: %v", err) 969 } 970 971 // Now create a source for that stream, either in same or remote cluster. 972 s2 := sc.clusters[test.sourceInCluster].randomServer() 973 nc2, js2 := jsClientConnect(t, s2) 974 defer nc2.Close() 975 976 if _, err := js2.AddStream(&nats.StreamConfig{ 977 Name: test.sourceName, 978 Storage: nats.FileStorage, 979 Sources: []*nats.StreamSource{{Name: test.streamName}}, 980 Replicas: 1, 981 }); err != nil { 982 t.Fatalf("Error adding source stream: %v", err) 983 } 984 985 // Check that TEST(n) has 1 consumer and that S(n) is created and has 1 message. 986 checkFor(t, 2*time.Second, 100*time.Millisecond, func() error { 987 si, err := js2.StreamInfo(test.sourceName) 988 if err != nil { 989 return fmt.Errorf("Could not get stream info: %v", err) 990 } 991 if si.State.Msgs != 1 { 992 return fmt.Errorf("Expected 1 msg, got state: %+v", si.State) 993 } 994 return nil 995 }) 996 997 // Get the consumer because we will want to artificially reduce 998 // the delete threshold. 999 leader := sc.clusters[0].streamLeader("$G", test.streamName) 1000 mset, err := leader.GlobalAccount().lookupStream(test.streamName) 1001 if err != nil { 1002 t.Fatalf("Expected to find a stream for %q, got %v", test.streamName, err) 1003 } 1004 cons := mset.getConsumers()[0] 1005 cons.mu.Lock() 1006 cons.dthresh = 1250 * time.Millisecond 1007 active := cons.active 1008 dtimerSet := cons.dtmr != nil 1009 deliver := cons.cfg.DeliverSubject 1010 cons.mu.Unlock() 1011 1012 if !active || dtimerSet { 1013 t.Fatalf("Invalid values for active=%v dtimerSet=%v", active, dtimerSet) 1014 } 1015 // To add to the mix, let's create a local interest on the delivery subject 1016 // and stop it. This is to ensure that this does not stop timers that should 1017 // still be running and monitor the GW interest. 1018 sub := natsSubSync(t, nc, deliver) 1019 natsFlush(t, nc) 1020 natsUnsub(t, sub) 1021 natsFlush(t, nc) 1022 1023 // Now remove the "S(n)" stream... 1024 if err := js2.DeleteStream(test.sourceName); err != nil { 1025 t.Fatalf("Error deleting stream: %v", err) 1026 } 1027 1028 // Now check that the stream S(n) is really removed and that 1029 // the consumer is gone for stream TEST(n). 1030 checkFor(t, 5*time.Second, 25*time.Millisecond, func() error { 1031 // First, make sure that stream S(n) has disappeared. 1032 if _, err := js2.StreamInfo(test.sourceName); err == nil { 1033 return fmt.Errorf("Stream %q should no longer exist", test.sourceName) 1034 } 1035 if ndc := mset.numDirectConsumers(); ndc != 0 { 1036 return fmt.Errorf("Expected %q stream to have 0 consumers, got %v", test.streamName, ndc) 1037 } 1038 return nil 1039 }) 1040 }) 1041 } 1042 } 1043 1044 func TestJetStreamSuperClusterGetNextSubRace(t *testing.T) { 1045 sc := createJetStreamSuperClusterWithTemplate(t, jsClusterAccountsTempl, 2, 2) 1046 defer sc.shutdown() 1047 1048 // Will connect the leafnode to cluster C1. We will then connect the "client" to cluster C2 to cross gateways. 1049 ln := sc.clusterForName("C1").createSingleLeafNodeNoSystemAccountAndEnablesJetStreamWithDomain("C", "nojs") 1050 defer ln.Shutdown() 1051 1052 // Shutdown 1 of the server from C1, (the one LN is not connected to) 1053 for _, s := range sc.clusterForName("C1").servers { 1054 s.mu.Lock() 1055 if len(s.leafs) == 0 { 1056 s.mu.Unlock() 1057 s.Shutdown() 1058 break 1059 } 1060 s.mu.Unlock() 1061 } 1062 1063 // Wait on meta leader in case shutdown of server above caused an election. 1064 sc.waitOnLeader() 1065 1066 var c2Srv *Server 1067 // Take the server from C2 that has no inbound from C1. 1068 c2 := sc.clusterForName("C2") 1069 for _, s := range c2.servers { 1070 var gwsa [2]*client 1071 gws := gwsa[:0] 1072 s.getInboundGatewayConnections(&gws) 1073 if len(gws) == 0 { 1074 c2Srv = s 1075 break 1076 } 1077 } 1078 if c2Srv == nil { 1079 t.Fatalf("Both servers in C2 had an inbound GW connection!") 1080 } 1081 1082 nc, js := jsClientConnectEx(t, c2Srv, []nats.JSOpt{nats.Domain("C")}, nats.UserInfo("nojs", "p")) 1083 defer nc.Close() 1084 1085 _, err := js.AddStream(&nats.StreamConfig{Name: "foo"}) 1086 require_NoError(t, err) 1087 1088 _, err = js.AddConsumer("foo", &nats.ConsumerConfig{Durable: "dur", AckPolicy: nats.AckExplicitPolicy}) 1089 require_NoError(t, err) 1090 1091 for i := 0; i < 100; i++ { 1092 sendStreamMsg(t, nc, "foo", "ok") 1093 } 1094 1095 // Wait for all messages to appear in the consumer 1096 checkFor(t, 2*time.Second, 50*time.Millisecond, func() error { 1097 ci, err := js.ConsumerInfo("foo", "dur") 1098 if err != nil { 1099 return err 1100 } 1101 if n := ci.NumPending; n != 100 { 1102 return fmt.Errorf("Expected 100 msgs, got %v", n) 1103 } 1104 return nil 1105 }) 1106 1107 req := &JSApiConsumerGetNextRequest{Batch: 1, Expires: 5 * time.Second} 1108 jreq, err := json.Marshal(req) 1109 require_NoError(t, err) 1110 // Create this by hand here to make sure we create the subscription 1111 // on the reply subject for every single request 1112 nextSubj := fmt.Sprintf(JSApiRequestNextT, "foo", "dur") 1113 nextSubj = "$JS.C.API" + strings.TrimPrefix(nextSubj, "$JS.API") 1114 for i := 0; i < 100; i++ { 1115 inbox := nats.NewInbox() 1116 sub := natsSubSync(t, nc, inbox) 1117 natsPubReq(t, nc, nextSubj, inbox, jreq) 1118 msg := natsNexMsg(t, sub, time.Second) 1119 if len(msg.Header) != 0 && string(msg.Data) != "ok" { 1120 t.Fatalf("Unexpected message: header=%+v data=%s", msg.Header, msg.Data) 1121 } 1122 sub.Unsubscribe() 1123 } 1124 } 1125 1126 func TestJetStreamSuperClusterPullConsumerAndHeaders(t *testing.T) { 1127 sc := createJetStreamSuperCluster(t, 3, 2) 1128 defer sc.shutdown() 1129 1130 c1 := sc.clusterForName("C1") 1131 c2 := sc.clusterForName("C2") 1132 1133 nc, js := jsClientConnect(t, c1.randomServer()) 1134 defer nc.Close() 1135 1136 if _, err := js.AddStream(&nats.StreamConfig{Name: "ORIGIN"}); err != nil { 1137 t.Fatalf("Unexpected error: %v", err) 1138 } 1139 toSend := 50 1140 for i := 0; i < toSend; i++ { 1141 if _, err := js.Publish("ORIGIN", []byte("ok")); err != nil { 1142 t.Fatalf("Unexpected publish error: %v", err) 1143 } 1144 } 1145 1146 nc2, js2 := jsClientConnect(t, c2.randomServer()) 1147 defer nc2.Close() 1148 1149 _, err := js2.AddStream(&nats.StreamConfig{ 1150 Name: "S", 1151 Sources: []*nats.StreamSource{{Name: "ORIGIN"}}, 1152 }) 1153 if err != nil { 1154 t.Fatalf("Unexpected error: %v", err) 1155 } 1156 // Wait for them to be in the sourced stream. 1157 checkFor(t, 5*time.Second, 250*time.Millisecond, func() error { 1158 if si, _ := js2.StreamInfo("S"); si.State.Msgs != uint64(toSend) { 1159 return fmt.Errorf("Expected %d msgs for %q, got %d", toSend, "S", si.State.Msgs) 1160 } 1161 return nil 1162 }) 1163 1164 // Now create a pull consumer for the sourced stream. 1165 _, err = js2.AddConsumer("S", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy}) 1166 if err != nil { 1167 t.Fatalf("Unexpected error: %v", err) 1168 } 1169 1170 // Now we will connect and request the next message from each server in C1 cluster and check that headers remain in place. 1171 for _, s := range c1.servers { 1172 nc, err := nats.Connect(s.ClientURL()) 1173 if err != nil { 1174 t.Fatalf("Unexpected error: %v", err) 1175 } 1176 defer nc.Close() 1177 m, err := nc.Request("$JS.API.CONSUMER.MSG.NEXT.S.dlc", nil, 2*time.Second) 1178 if err != nil { 1179 t.Fatalf("Unexpected error: %v", err) 1180 } 1181 if len(m.Header) != 1 { 1182 t.Fatalf("Expected 1 header element, got %+v", m.Header) 1183 } 1184 } 1185 } 1186 1187 func TestJetStreamSuperClusterStatszActiveServers(t *testing.T) { 1188 sc := createJetStreamSuperCluster(t, 2, 2) 1189 defer sc.shutdown() 1190 1191 checkActive := func(expected int) { 1192 t.Helper() 1193 checkFor(t, 10*time.Second, 500*time.Millisecond, func() error { 1194 s := sc.randomServer() 1195 nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 1196 if err != nil { 1197 t.Fatalf("Failed to create system client: %v", err) 1198 } 1199 defer nc.Close() 1200 1201 resp, err := nc.Request(serverStatsPingReqSubj, nil, time.Second) 1202 if err != nil { 1203 t.Fatalf("Unexpected error: %v", err) 1204 } 1205 var ssm ServerStatsMsg 1206 if err := json.Unmarshal(resp.Data, &ssm); err != nil { 1207 t.Fatalf("Unexpected error: %v", err) 1208 } 1209 if ssm.Stats.ActiveServers != expected { 1210 return fmt.Errorf("Wanted %d, got %d", expected, ssm.Stats.ActiveServers) 1211 } 1212 return nil 1213 }) 1214 } 1215 1216 checkActive(4) 1217 c := sc.randomCluster() 1218 ss := c.randomServer() 1219 ss.Shutdown() 1220 checkActive(3) 1221 c.restartServer(ss) 1222 checkActive(4) 1223 } 1224 1225 func TestJetStreamSuperClusterSourceAndMirrorConsumersLeaderChange(t *testing.T) { 1226 sc := createJetStreamSuperCluster(t, 3, 2) 1227 defer sc.shutdown() 1228 1229 c1 := sc.clusterForName("C1") 1230 c2 := sc.clusterForName("C2") 1231 1232 nc, js := jsClientConnect(t, c1.randomServer()) 1233 defer nc.Close() 1234 1235 var sources []*nats.StreamSource 1236 numStreams := 10 1237 1238 for i := 1; i <= numStreams; i++ { 1239 name := fmt.Sprintf("O%d", i) 1240 sources = append(sources, &nats.StreamSource{Name: name}) 1241 if _, err := js.AddStream(&nats.StreamConfig{Name: name}); err != nil { 1242 t.Fatalf("Unexpected error: %v", err) 1243 } 1244 } 1245 1246 // Place our new stream that will source all the others in different cluster. 1247 nc, js = jsClientConnect(t, c2.randomServer()) 1248 defer nc.Close() 1249 1250 _, err := js.AddStream(&nats.StreamConfig{ 1251 Name: "S", 1252 Replicas: 2, 1253 Sources: sources, 1254 }) 1255 if err != nil { 1256 t.Fatalf("Unexpected error: %v", err) 1257 } 1258 1259 // Force leader change twice. 1260 nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "S"), nil, time.Second) 1261 c2.waitOnStreamLeader("$G", "S") 1262 nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "S"), nil, time.Second) 1263 c2.waitOnStreamLeader("$G", "S") 1264 1265 // Now make sure we only have a single direct consumer on our origin streams. 1266 // Pick one at random. 1267 name := fmt.Sprintf("O%d", rand.Intn(numStreams-1)+1) 1268 c1.waitOnStreamLeader("$G", name) 1269 s := c1.streamLeader("$G", name) 1270 a, err := s.lookupAccount("$G") 1271 if err != nil { 1272 t.Fatalf("Unexpected error: %v", err) 1273 } 1274 mset, err := a.lookupStream(name) 1275 if err != nil { 1276 t.Fatalf("Unexpected error: %v", err) 1277 } 1278 1279 checkFor(t, 10*time.Second, 250*time.Millisecond, func() error { 1280 if ndc := mset.numDirectConsumers(); ndc != 1 { 1281 return fmt.Errorf("Stream %q wanted 1 direct consumer, got %d", name, ndc) 1282 } 1283 return nil 1284 }) 1285 1286 // Now create a mirror of selected from above. Will test same scenario. 1287 _, err = js.AddStream(&nats.StreamConfig{ 1288 Name: "M", 1289 Replicas: 2, 1290 Mirror: &nats.StreamSource{Name: name}, 1291 }) 1292 if err != nil { 1293 t.Fatalf("Unexpected error: %v", err) 1294 } 1295 // Force leader change twice. 1296 nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "M"), nil, time.Second) 1297 c2.waitOnStreamLeader("$G", "M") 1298 nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "M"), nil, time.Second) 1299 c2.waitOnStreamLeader("$G", "M") 1300 1301 checkFor(t, 10*time.Second, 250*time.Millisecond, func() error { 1302 if ndc := mset.numDirectConsumers(); ndc != 2 { 1303 return fmt.Errorf("Stream %q wanted 2 direct consumers, got %d", name, ndc) 1304 } 1305 return nil 1306 }) 1307 } 1308 1309 func TestJetStreamSuperClusterPushConsumerInterest(t *testing.T) { 1310 sc := createJetStreamSuperCluster(t, 3, 2) 1311 defer sc.shutdown() 1312 1313 for _, test := range []struct { 1314 name string 1315 queue string 1316 }{ 1317 {"non queue", _EMPTY_}, 1318 {"queue", "queue"}, 1319 } { 1320 t.Run(test.name, func(t *testing.T) { 1321 testInterest := func(s *Server) { 1322 t.Helper() 1323 nc, js := jsClientConnect(t, s) 1324 defer nc.Close() 1325 1326 _, err := js.AddStream(&nats.StreamConfig{ 1327 Name: "TEST", 1328 Subjects: []string{"foo"}, 1329 Replicas: 3, 1330 }) 1331 require_NoError(t, err) 1332 1333 var sub *nats.Subscription 1334 if test.queue != _EMPTY_ { 1335 sub, err = js.QueueSubscribeSync("foo", test.queue) 1336 } else { 1337 sub, err = js.SubscribeSync("foo", nats.Durable("dur")) 1338 } 1339 require_NoError(t, err) 1340 1341 js.Publish("foo", []byte("msg1")) 1342 // Since the GW watcher is checking every 1sec, make sure we are 1343 // giving it enough time for the delivery to start. 1344 _, err = sub.NextMsg(2 * time.Second) 1345 require_NoError(t, err) 1346 } 1347 1348 // Create the durable push consumer from cluster "0" 1349 testInterest(sc.clusters[0].servers[0]) 1350 1351 // Now "move" to a server in cluster "1" 1352 testInterest(sc.clusters[1].servers[0]) 1353 }) 1354 } 1355 } 1356 1357 func TestJetStreamSuperClusterOverflowPlacement(t *testing.T) { 1358 sc := createJetStreamSuperClusterWithTemplate(t, jsClusterMaxBytesTempl, 3, 3) 1359 defer sc.shutdown() 1360 1361 pcn := "C2" 1362 s := sc.clusterForName(pcn).randomServer() 1363 nc, js := jsClientConnect(t, s) 1364 defer nc.Close() 1365 1366 // With this setup, we opted in for requiring MaxBytes, so this should error. 1367 _, err := js.AddStream(&nats.StreamConfig{ 1368 Name: "foo", 1369 Replicas: 3, 1370 }) 1371 require_Error(t, err, NewJSStreamMaxBytesRequiredError()) 1372 1373 // R=2 on purpose to leave one server empty. 1374 _, err = js.AddStream(&nats.StreamConfig{ 1375 Name: "foo", 1376 Replicas: 2, 1377 MaxBytes: 2 * 1024 * 1024 * 1024, 1378 }) 1379 require_NoError(t, err) 1380 1381 // Now try to add another that will overflow the current cluster's reservation. 1382 // Since we asked explicitly for the same cluster this should fail. 1383 // Note this will not be testing the peer picker since the update has probably not made it to the meta leader. 1384 _, err = js.AddStream(&nats.StreamConfig{ 1385 Name: "bar", 1386 Replicas: 3, 1387 MaxBytes: 2 * 1024 * 1024 * 1024, 1388 Placement: &nats.Placement{Cluster: pcn}, 1389 }) 1390 require_Contains(t, err.Error(), "nats: no suitable peers for placement") 1391 // Now test actual overflow placement. So try again with no placement designation. 1392 // This will test the peer picker's logic since they are updated at this point and the meta leader 1393 // knows it can not place it in C2. 1394 si, err := js.AddStream(&nats.StreamConfig{ 1395 Name: "bar", 1396 Replicas: 3, 1397 MaxBytes: 2 * 1024 * 1024 * 1024, 1398 }) 1399 require_NoError(t, err) 1400 1401 // Make sure we did not get place into C2. 1402 falt := si.Cluster.Name 1403 if falt == pcn { 1404 t.Fatalf("Expected to be placed in another cluster besides %q, but got %q", pcn, falt) 1405 } 1406 1407 // One more time that should spill over again to our last cluster. 1408 si, err = js.AddStream(&nats.StreamConfig{ 1409 Name: "baz", 1410 Replicas: 3, 1411 MaxBytes: 2 * 1024 * 1024 * 1024, 1412 }) 1413 require_NoError(t, err) 1414 1415 // Make sure we did not get place into C2. 1416 if salt := si.Cluster.Name; salt == pcn || salt == falt { 1417 t.Fatalf("Expected to be placed in last cluster besides %q or %q, but got %q", pcn, falt, salt) 1418 } 1419 1420 // Now place a stream of R1 into C2 which should have space. 1421 si, err = js.AddStream(&nats.StreamConfig{ 1422 Name: "dlc", 1423 MaxBytes: 2 * 1024 * 1024 * 1024, 1424 }) 1425 require_NoError(t, err) 1426 1427 if si.Cluster.Name != pcn { 1428 t.Fatalf("Expected to be placed in our origin cluster %q, but got %q", pcn, si.Cluster.Name) 1429 } 1430 } 1431 1432 func TestJetStreamSuperClusterConcurrentOverflow(t *testing.T) { 1433 sc := createJetStreamSuperClusterWithTemplate(t, jsClusterMaxBytesTempl, 3, 3) 1434 defer sc.shutdown() 1435 1436 pcn := "C2" 1437 1438 startCh := make(chan bool) 1439 var wg sync.WaitGroup 1440 var swg sync.WaitGroup 1441 1442 start := func(name string) { 1443 defer wg.Done() 1444 1445 s := sc.clusterForName(pcn).randomServer() 1446 nc, js := jsClientConnect(t, s) 1447 defer nc.Close() 1448 1449 swg.Done() 1450 <-startCh 1451 1452 _, err := js.AddStream(&nats.StreamConfig{ 1453 Name: name, 1454 Replicas: 3, 1455 MaxBytes: 2 * 1024 * 1024 * 1024, 1456 }) 1457 require_NoError(t, err) 1458 } 1459 wg.Add(2) 1460 swg.Add(2) 1461 go start("foo") 1462 go start("bar") 1463 swg.Wait() 1464 // Now start both at same time. 1465 close(startCh) 1466 wg.Wait() 1467 } 1468 1469 func TestJetStreamSuperClusterStreamTagPlacement(t *testing.T) { 1470 sc := createJetStreamTaggedSuperCluster(t) 1471 defer sc.shutdown() 1472 1473 placeOK := func(connectCluster string, tags []string, expectedCluster string) { 1474 t.Helper() 1475 nc, js := jsClientConnect(t, sc.clusterForName(connectCluster).randomServer()) 1476 defer nc.Close() 1477 si, err := js.AddStream(&nats.StreamConfig{ 1478 Name: "TEST", 1479 Subjects: []string{"foo"}, 1480 Placement: &nats.Placement{Tags: tags}, 1481 }) 1482 require_NoError(t, err) 1483 if si.Cluster.Name != expectedCluster { 1484 t.Fatalf("Failed to place properly in %q, got %q", expectedCluster, si.Cluster.Name) 1485 } 1486 js.DeleteStream("TEST") 1487 } 1488 1489 placeOK("C2", []string{"cloud:aws"}, "C1") 1490 placeOK("C2", []string{"country:jp"}, "C3") 1491 placeOK("C1", []string{"cloud:gcp", "country:uk"}, "C2") 1492 1493 // Case shoud not matter. 1494 placeOK("C1", []string{"cloud:GCP", "country:UK"}, "C2") 1495 placeOK("C2", []string{"Cloud:AwS", "Country:uS"}, "C1") 1496 1497 placeErr := func(connectCluster string, tags []string) { 1498 t.Helper() 1499 nc, js := jsClientConnect(t, sc.clusterForName(connectCluster).randomServer()) 1500 defer nc.Close() 1501 _, err := js.AddStream(&nats.StreamConfig{ 1502 Name: "TEST", 1503 Subjects: []string{"foo"}, 1504 Placement: &nats.Placement{Tags: tags}, 1505 }) 1506 require_Contains(t, err.Error(), "no suitable peers for placement", "tags not matched") 1507 require_Contains(t, err.Error(), tags...) 1508 } 1509 1510 placeErr("C1", []string{"cloud:GCP", "country:US"}) 1511 placeErr("C1", []string{"country:DN"}) 1512 placeErr("C1", []string{"cloud:DO"}) 1513 } 1514 1515 func TestJetStreamSuperClusterRemovedPeersAndStreamsListAndDelete(t *testing.T) { 1516 sc := createJetStreamSuperCluster(t, 3, 3) 1517 defer sc.shutdown() 1518 1519 pcn := "C2" 1520 sc.waitOnLeader() 1521 ml := sc.leader() 1522 if ml.ClusterName() == pcn { 1523 pcn = "C1" 1524 } 1525 1526 // Client based API 1527 nc, js := jsClientConnect(t, ml) 1528 defer nc.Close() 1529 1530 _, err := js.AddStream(&nats.StreamConfig{ 1531 Name: "GONE", 1532 Replicas: 3, 1533 Placement: &nats.Placement{Cluster: pcn}, 1534 }) 1535 require_NoError(t, err) 1536 1537 _, err = js.AddConsumer("GONE", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy}) 1538 require_NoError(t, err) 1539 1540 _, err = js.AddStream(&nats.StreamConfig{ 1541 Name: "TEST", 1542 Replicas: 3, 1543 Placement: &nats.Placement{Cluster: ml.ClusterName()}, 1544 }) 1545 require_NoError(t, err) 1546 1547 // Put messages in.. 1548 num := 100 1549 for i := 0; i < num; i++ { 1550 js.PublishAsync("GONE", []byte("SLS")) 1551 js.PublishAsync("TEST", []byte("SLS")) 1552 } 1553 select { 1554 case <-js.PublishAsyncComplete(): 1555 case <-time.After(5 * time.Second): 1556 t.Fatalf("Did not receive completion signal") 1557 } 1558 1559 c := sc.clusterForName(pcn) 1560 c.shutdown() 1561 1562 // Grab Stream List.. 1563 start := time.Now() 1564 resp, err := nc.Request(JSApiStreamList, nil, 2*time.Second) 1565 require_NoError(t, err) 1566 if delta := time.Since(start); delta > 100*time.Millisecond { 1567 t.Fatalf("Stream list call took too long to return: %v", delta) 1568 } 1569 var list JSApiStreamListResponse 1570 err = json.Unmarshal(resp.Data, &list) 1571 require_NoError(t, err) 1572 1573 if len(list.Missing) != 1 || list.Missing[0] != "GONE" { 1574 t.Fatalf("Wrong Missing: %+v", list) 1575 } 1576 1577 // Check behavior of stream info as well. We want it to return the stream is offline and not just timeout. 1578 _, err = js.StreamInfo("GONE") 1579 // FIXME(dlc) - Go client not putting nats: prefix on for stream but does for consumer. 1580 require_Error(t, err, NewJSStreamOfflineError(), errors.New("nats: stream is offline")) 1581 1582 // Same for Consumer 1583 start = time.Now() 1584 resp, err = nc.Request("$JS.API.CONSUMER.LIST.GONE", nil, 2*time.Second) 1585 require_NoError(t, err) 1586 if delta := time.Since(start); delta > 100*time.Millisecond { 1587 t.Fatalf("Consumer list call took too long to return: %v", delta) 1588 } 1589 var clist JSApiConsumerListResponse 1590 err = json.Unmarshal(resp.Data, &clist) 1591 require_NoError(t, err) 1592 1593 if len(clist.Missing) != 1 || clist.Missing[0] != "dlc" { 1594 t.Fatalf("Wrong Missing: %+v", clist) 1595 } 1596 1597 _, err = js.ConsumerInfo("GONE", "dlc") 1598 require_Error(t, err, NewJSConsumerOfflineError(), errors.New("nats: consumer is offline")) 1599 1600 // Make sure delete works. 1601 err = js.DeleteConsumer("GONE", "dlc") 1602 require_NoError(t, err) 1603 1604 err = js.DeleteStream("GONE") 1605 require_NoError(t, err) 1606 1607 // Test it is really gone. 1608 _, err = js.StreamInfo("GONE") 1609 require_Error(t, err, nats.ErrStreamNotFound) 1610 } 1611 1612 func TestJetStreamSuperClusterConsumerDeliverNewBug(t *testing.T) { 1613 sc := createJetStreamSuperCluster(t, 3, 3) 1614 defer sc.shutdown() 1615 1616 pcn := "C2" 1617 sc.waitOnLeader() 1618 ml := sc.leader() 1619 if ml.ClusterName() == pcn { 1620 pcn = "C1" 1621 } 1622 1623 // Client based API 1624 nc, js := jsClientConnect(t, ml) 1625 defer nc.Close() 1626 1627 _, err := js.AddStream(&nats.StreamConfig{ 1628 Name: "T", 1629 Replicas: 3, 1630 Placement: &nats.Placement{Cluster: pcn}, 1631 }) 1632 require_NoError(t, err) 1633 1634 // Put messages in.. 1635 num := 100 1636 for i := 0; i < num; i++ { 1637 js.PublishAsync("T", []byte("OK")) 1638 } 1639 select { 1640 case <-js.PublishAsyncComplete(): 1641 case <-time.After(5 * time.Second): 1642 t.Fatalf("Did not receive completion signal") 1643 } 1644 1645 ci, err := js.AddConsumer("T", &nats.ConsumerConfig{ 1646 Durable: "d", 1647 AckPolicy: nats.AckExplicitPolicy, 1648 DeliverPolicy: nats.DeliverNewPolicy, 1649 }) 1650 require_NoError(t, err) 1651 1652 if ci.Delivered.Consumer != 0 || ci.Delivered.Stream != 100 { 1653 t.Fatalf("Incorrect consumer delivered info: %+v", ci.Delivered) 1654 } 1655 1656 c := sc.clusterForName(pcn) 1657 for _, s := range c.servers { 1658 sd := s.JetStreamConfig().StoreDir 1659 s.Shutdown() 1660 removeDir(t, sd) 1661 s = c.restartServer(s) 1662 c.waitOnServerHealthz(s) 1663 c.waitOnConsumerLeader("$G", "T", "d") 1664 } 1665 1666 c.waitOnConsumerLeader("$G", "T", "d") 1667 ci, err = js.ConsumerInfo("T", "d") 1668 require_NoError(t, err) 1669 1670 if ci.Delivered.Consumer != 0 || ci.Delivered.Stream != 100 { 1671 t.Fatalf("Incorrect consumer delivered info: %+v", ci.Delivered) 1672 } 1673 if ci.NumPending != 0 { 1674 t.Fatalf("Did not expect NumPending, got %d", ci.NumPending) 1675 } 1676 } 1677 1678 // This will test our ability to move streams and consumers between clusters. 1679 func TestJetStreamSuperClusterMovingStreamsAndConsumers(t *testing.T) { 1680 sc := createJetStreamTaggedSuperCluster(t) 1681 defer sc.shutdown() 1682 1683 nc, js := jsClientConnect(t, sc.randomServer()) 1684 defer nc.Close() 1685 1686 for _, test := range []struct { 1687 name string 1688 replicas int 1689 }{ 1690 {"R1", 1}, 1691 {"R3", 3}, 1692 } { 1693 t.Run(test.name, func(t *testing.T) { 1694 replicas := test.replicas 1695 1696 si, err := js.AddStream(&nats.StreamConfig{ 1697 Name: "MOVE", 1698 Replicas: replicas, 1699 Placement: &nats.Placement{Tags: []string{"cloud:aws"}}, 1700 }) 1701 require_NoError(t, err) 1702 defer js.DeleteStream("MOVE") 1703 1704 if si.Cluster.Name != "C1" { 1705 t.Fatalf("Failed to place properly in %q, got %q", "C1", si.Cluster.Name) 1706 } 1707 1708 for i := 0; i < 1000; i++ { 1709 _, err := js.PublishAsync("MOVE", []byte("Moving on up")) 1710 require_NoError(t, err) 1711 } 1712 select { 1713 case <-js.PublishAsyncComplete(): 1714 case <-time.After(5 * time.Second): 1715 t.Fatalf("Did not receive completion signal") 1716 } 1717 1718 // Durable Push Consumer, so same R. 1719 dpushSub, err := js.SubscribeSync("MOVE", nats.Durable("dlc")) 1720 require_NoError(t, err) 1721 defer dpushSub.Unsubscribe() 1722 1723 // Ephemeral Push Consumer, R1. 1724 epushSub, err := js.SubscribeSync("MOVE") 1725 require_NoError(t, err) 1726 defer epushSub.Unsubscribe() 1727 1728 // Durable Pull Consumer, so same R. 1729 dpullSub, err := js.PullSubscribe("MOVE", "dlc-pull") 1730 require_NoError(t, err) 1731 defer dpullSub.Unsubscribe() 1732 1733 // TODO(dlc) - Server supports ephemeral pulls but Go client does not yet. 1734 1735 si, err = js.StreamInfo("MOVE") 1736 require_NoError(t, err) 1737 if si.State.Consumers != 3 { 1738 t.Fatalf("Expected 3 attached consumers, got %d", si.State.Consumers) 1739 } 1740 1741 initialState := si.State 1742 1743 checkSubsPending(t, dpushSub, int(initialState.Msgs)) 1744 checkSubsPending(t, epushSub, int(initialState.Msgs)) 1745 1746 // Ack 100 1747 toAck := 100 1748 for i := 0; i < toAck; i++ { 1749 m, err := dpushSub.NextMsg(time.Second) 1750 require_NoError(t, err) 1751 m.AckSync() 1752 // Ephemeral 1753 m, err = epushSub.NextMsg(time.Second) 1754 require_NoError(t, err) 1755 m.AckSync() 1756 } 1757 1758 // Do same with pull subscriber. 1759 for _, m := range fetchMsgs(t, dpullSub, toAck, 5*time.Second) { 1760 m.AckSync() 1761 } 1762 1763 // First make sure we disallow move and replica changes in same update. 1764 _, err = js.UpdateStream(&nats.StreamConfig{ 1765 Name: "MOVE", 1766 Placement: &nats.Placement{Tags: []string{"cloud:gcp"}}, 1767 Replicas: replicas + 1, 1768 }) 1769 require_Error(t, err, NewJSStreamMoveAndScaleError()) 1770 1771 // Now move to new cluster. 1772 si, err = js.UpdateStream(&nats.StreamConfig{ 1773 Name: "MOVE", 1774 Replicas: replicas, 1775 Placement: &nats.Placement{Tags: []string{"cloud:gcp"}}, 1776 }) 1777 require_NoError(t, err) 1778 1779 if si.Cluster.Name != "C1" { 1780 t.Fatalf("Expected cluster of %q but got %q", "C1", si.Cluster.Name) 1781 } 1782 1783 // Make sure we can not move an inflight stream and consumers, should error. 1784 _, err = js.UpdateStream(&nats.StreamConfig{ 1785 Name: "MOVE", 1786 Replicas: replicas, 1787 Placement: &nats.Placement{Tags: []string{"cloud:aws"}}, 1788 }) 1789 require_Contains(t, err.Error(), "stream move already in progress") 1790 1791 checkFor(t, 10*time.Second, 200*time.Millisecond, func() error { 1792 si, err := js.StreamInfo("MOVE", nats.MaxWait(500*time.Millisecond)) 1793 if err != nil { 1794 return err 1795 } 1796 // We should see 2X peers. 1797 numPeers := len(si.Cluster.Replicas) 1798 if si.Cluster.Leader != _EMPTY_ { 1799 numPeers++ 1800 } 1801 if numPeers != 2*replicas { 1802 // The move can happen very quick now, so we might already be done. 1803 if si.Cluster.Name == "C2" { 1804 return nil 1805 } 1806 return fmt.Errorf("Expected to see %d replicas, got %d", 2*replicas, numPeers) 1807 } 1808 return nil 1809 }) 1810 1811 // Expect a new leader to emerge and replicas to drop as a leader is elected. 1812 // We have to check fast or it might complete and we will not see intermediate steps. 1813 sc.waitOnStreamLeader("$G", "MOVE") 1814 checkFor(t, 10*time.Second, 200*time.Millisecond, func() error { 1815 si, err := js.StreamInfo("MOVE", nats.MaxWait(500*time.Millisecond)) 1816 if err != nil { 1817 return err 1818 } 1819 if len(si.Cluster.Replicas) >= 2*replicas { 1820 return fmt.Errorf("Expected <%d replicas, got %d", 2*replicas, len(si.Cluster.Replicas)) 1821 } 1822 return nil 1823 }) 1824 1825 // Should see the cluster designation and leader switch to C2. 1826 // We should also shrink back down to original replica count. 1827 checkFor(t, 20*time.Second, 200*time.Millisecond, func() error { 1828 si, err := js.StreamInfo("MOVE", nats.MaxWait(500*time.Millisecond)) 1829 if err != nil { 1830 return err 1831 } 1832 if si.Cluster.Name != "C2" { 1833 return fmt.Errorf("Wrong cluster: %q", si.Cluster.Name) 1834 } 1835 if si.Cluster.Leader == _EMPTY_ { 1836 return fmt.Errorf("No leader yet") 1837 } else if !strings.HasPrefix(si.Cluster.Leader, "C2-") { 1838 return fmt.Errorf("Wrong leader: %q", si.Cluster.Leader) 1839 } 1840 // Now we want to see that we shrink back to original. 1841 if len(si.Cluster.Replicas) != replicas-1 { 1842 return fmt.Errorf("Expected %d replicas, got %d", replicas-1, len(si.Cluster.Replicas)) 1843 } 1844 return nil 1845 }) 1846 1847 // Check moved state is same as initial state. 1848 si, err = js.StreamInfo("MOVE") 1849 require_NoError(t, err) 1850 1851 if !reflect.DeepEqual(si.State, initialState) { 1852 t.Fatalf("States do not match after migration:\n%+v\nvs\n%+v", si.State, initialState) 1853 } 1854 1855 // Make sure we can still send messages. 1856 addN := toAck 1857 for i := 0; i < addN; i++ { 1858 _, err := js.Publish("MOVE", []byte("Done Moved")) 1859 require_NoError(t, err) 1860 } 1861 1862 si, err = js.StreamInfo("MOVE") 1863 require_NoError(t, err) 1864 1865 expectedPushMsgs := initialState.Msgs + uint64(addN) 1866 expectedPullMsgs := uint64(addN) 1867 1868 if si.State.Msgs != expectedPushMsgs { 1869 t.Fatalf("Expected to be able to send new messages") 1870 } 1871 1872 // Now check consumers, make sure the state is correct and that they transferred state and reflect the new messages. 1873 // We Ack'd 100 and sent another 100, so should be same. 1874 checkConsumer := func(sub *nats.Subscription, isPull bool) { 1875 t.Helper() 1876 checkFor(t, 10*time.Second, 100*time.Millisecond, func() error { 1877 ci, err := sub.ConsumerInfo() 1878 if err != nil { 1879 return err 1880 } 1881 var expectedMsgs uint64 1882 if isPull { 1883 expectedMsgs = expectedPullMsgs 1884 } else { 1885 expectedMsgs = expectedPushMsgs 1886 } 1887 1888 if ci.Delivered.Consumer != expectedMsgs || ci.Delivered.Stream != expectedMsgs { 1889 return fmt.Errorf("Delivered for %q is not correct: %+v", ci.Name, ci.Delivered) 1890 } 1891 if ci.AckFloor.Consumer != uint64(toAck) || ci.AckFloor.Stream != uint64(toAck) { 1892 return fmt.Errorf("AckFloor for %q is not correct: %+v", ci.Name, ci.AckFloor) 1893 } 1894 if isPull && ci.NumAckPending != 0 { 1895 return fmt.Errorf("NumAckPending for %q is not correct: %v", ci.Name, ci.NumAckPending) 1896 } else if !isPull && ci.NumAckPending != int(initialState.Msgs) { 1897 return fmt.Errorf("NumAckPending for %q is not correct: %v", ci.Name, ci.NumAckPending) 1898 } 1899 // Make sure the replicas etc are back to what is expected. 1900 si, err := js.StreamInfo("MOVE") 1901 if err != nil { 1902 return err 1903 } 1904 numExpected := si.Config.Replicas 1905 if ci.Config.Durable == _EMPTY_ { 1906 numExpected = 1 1907 } 1908 numPeers := len(ci.Cluster.Replicas) 1909 if ci.Cluster.Leader != _EMPTY_ { 1910 numPeers++ 1911 } 1912 if numPeers != numExpected { 1913 return fmt.Errorf("Expected %d peers, got %d", numExpected, numPeers) 1914 } 1915 // If we are push check sub pending. 1916 if !isPull { 1917 checkSubsPending(t, sub, int(expectedPushMsgs)-toAck) 1918 } 1919 return nil 1920 }) 1921 } 1922 1923 checkPushConsumer := func(sub *nats.Subscription) { 1924 t.Helper() 1925 checkConsumer(sub, false) 1926 } 1927 checkPullConsumer := func(sub *nats.Subscription) { 1928 t.Helper() 1929 checkConsumer(sub, true) 1930 } 1931 1932 checkPushConsumer(dpushSub) 1933 checkPushConsumer(epushSub) 1934 checkPullConsumer(dpullSub) 1935 1936 // Cleanup 1937 err = js.DeleteStream("MOVE") 1938 require_NoError(t, err) 1939 }) 1940 } 1941 } 1942 1943 func TestJetStreamSuperClusterMovingStreamsWithMirror(t *testing.T) { 1944 sc := createJetStreamTaggedSuperCluster(t) 1945 defer sc.shutdown() 1946 1947 nc, js := jsClientConnect(t, sc.randomServer()) 1948 defer nc.Close() 1949 1950 _, err := js.AddStream(&nats.StreamConfig{ 1951 Name: "SOURCE", 1952 Subjects: []string{"foo", "bar"}, 1953 Replicas: 3, 1954 Placement: &nats.Placement{Tags: []string{"cloud:aws"}}, 1955 }) 1956 require_NoError(t, err) 1957 1958 _, err = js.AddStream(&nats.StreamConfig{ 1959 Name: "MIRROR", 1960 Replicas: 1, 1961 Mirror: &nats.StreamSource{Name: "SOURCE"}, 1962 Placement: &nats.Placement{Tags: []string{"cloud:gcp"}}, 1963 }) 1964 require_NoError(t, err) 1965 1966 done := make(chan struct{}) 1967 exited := make(chan struct{}) 1968 errors := make(chan error, 1) 1969 1970 numNoResp := uint64(0) 1971 1972 // We will run a separate routine and send at 100hz 1973 go func() { 1974 nc, js := jsClientConnect(t, sc.randomServer()) 1975 defer nc.Close() 1976 1977 defer close(exited) 1978 1979 for { 1980 select { 1981 case <-done: 1982 return 1983 case <-time.After(10 * time.Millisecond): 1984 _, err := js.Publish("foo", []byte("100HZ")) 1985 if err == nil { 1986 } else if err == nats.ErrNoStreamResponse { 1987 atomic.AddUint64(&numNoResp, 1) 1988 continue 1989 } 1990 if err != nil { 1991 errors <- err 1992 return 1993 } 1994 } 1995 } 1996 }() 1997 1998 // Let it get going. 1999 time.Sleep(500 * time.Millisecond) 2000 2001 // Now move the source to a new cluster. 2002 _, err = js.UpdateStream(&nats.StreamConfig{ 2003 Name: "SOURCE", 2004 Subjects: []string{"foo", "bar"}, 2005 Replicas: 3, 2006 Placement: &nats.Placement{Tags: []string{"cloud:gcp"}}, 2007 }) 2008 require_NoError(t, err) 2009 2010 checkFor(t, 30*time.Second, 100*time.Millisecond, func() error { 2011 si, err := js.StreamInfo("SOURCE") 2012 if err != nil { 2013 return err 2014 } 2015 if si.Cluster.Name != "C2" { 2016 return fmt.Errorf("Wrong cluster: %q", si.Cluster.Name) 2017 } 2018 if si.Cluster.Leader == _EMPTY_ { 2019 return fmt.Errorf("No leader yet") 2020 } else if !strings.HasPrefix(si.Cluster.Leader, "C2-") { 2021 return fmt.Errorf("Wrong leader: %q", si.Cluster.Leader) 2022 } 2023 // Now we want to see that we shrink back to original. 2024 if len(si.Cluster.Replicas) != 2 { 2025 return fmt.Errorf("Expected %d replicas, got %d", 2, len(si.Cluster.Replicas)) 2026 } 2027 // Let's get to 50+ msgs. 2028 if si.State.Msgs < 50 { 2029 return fmt.Errorf("Only see %d msgs", si.State.Msgs) 2030 } 2031 return nil 2032 }) 2033 2034 close(done) 2035 <-exited 2036 2037 if nnr := atomic.LoadUint64(&numNoResp); nnr > 0 { 2038 if nnr > 5 { 2039 t.Fatalf("Expected no or very few failed message publishes, got %d", nnr) 2040 } else { 2041 t.Logf("Got a few failed publishes: %d", nnr) 2042 } 2043 } 2044 2045 checkFor(t, 20*time.Second, 100*time.Millisecond, func() error { 2046 si, err := js.StreamInfo("SOURCE") 2047 require_NoError(t, err) 2048 mi, err := js.StreamInfo("MIRROR") 2049 require_NoError(t, err) 2050 2051 if !reflect.DeepEqual(si.State, mi.State) { 2052 return fmt.Errorf("Expected mirror to be the same, got %+v vs %+v", mi.State, si.State) 2053 } 2054 return nil 2055 }) 2056 2057 } 2058 2059 func TestJetStreamSuperClusterMovingStreamAndMoveBack(t *testing.T) { 2060 sc := createJetStreamTaggedSuperCluster(t) 2061 defer sc.shutdown() 2062 2063 nc, js := jsClientConnect(t, sc.randomServer()) 2064 defer nc.Close() 2065 2066 for _, test := range []struct { 2067 name string 2068 replicas int 2069 }{ 2070 {"R1", 1}, 2071 {"R3", 3}, 2072 } { 2073 t.Run(test.name, func(t *testing.T) { 2074 js.DeleteStream("TEST") 2075 2076 _, err := js.AddStream(&nats.StreamConfig{ 2077 Name: "TEST", 2078 Replicas: test.replicas, 2079 Placement: &nats.Placement{Tags: []string{"cloud:aws"}}, 2080 }) 2081 require_NoError(t, err) 2082 2083 toSend := 10_000 2084 for i := 0; i < toSend; i++ { 2085 _, err := js.Publish("TEST", []byte("HELLO WORLD")) 2086 require_NoError(t, err) 2087 } 2088 2089 _, err = js.UpdateStream(&nats.StreamConfig{ 2090 Name: "TEST", 2091 Replicas: test.replicas, 2092 Placement: &nats.Placement{Tags: []string{"cloud:gcp"}}, 2093 }) 2094 require_NoError(t, err) 2095 2096 checkMove := func(cluster string) { 2097 t.Helper() 2098 sc.waitOnStreamLeader("$G", "TEST") 2099 checkFor(t, 20*time.Second, 100*time.Millisecond, func() error { 2100 si, err := js.StreamInfo("TEST") 2101 if err != nil { 2102 return err 2103 } 2104 if si.Cluster.Name != cluster { 2105 return fmt.Errorf("Wrong cluster: %q", si.Cluster.Name) 2106 } 2107 if si.Cluster.Leader == _EMPTY_ { 2108 return fmt.Errorf("No leader yet") 2109 } else if !strings.HasPrefix(si.Cluster.Leader, cluster) { 2110 return fmt.Errorf("Wrong leader: %q", si.Cluster.Leader) 2111 } 2112 // Now we want to see that we shrink back to original. 2113 if len(si.Cluster.Replicas) != test.replicas-1 { 2114 return fmt.Errorf("Expected %d replicas, got %d", test.replicas-1, len(si.Cluster.Replicas)) 2115 } 2116 if si.State.Msgs != uint64(toSend) { 2117 return fmt.Errorf("Only see %d msgs", si.State.Msgs) 2118 } 2119 return nil 2120 }) 2121 } 2122 2123 checkMove("C2") 2124 2125 _, err = js.UpdateStream(&nats.StreamConfig{ 2126 Name: "TEST", 2127 Replicas: test.replicas, 2128 Placement: &nats.Placement{Tags: []string{"cloud:aws"}}, 2129 }) 2130 require_NoError(t, err) 2131 2132 checkMove("C1") 2133 }) 2134 } 2135 } 2136 2137 func TestJetStreamSuperClusterImportConsumerStreamSubjectRemap(t *testing.T) { 2138 template := ` 2139 listen: 127.0.0.1:-1 2140 server_name: %s 2141 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, domain: HUB, store_dir: '%s'} 2142 2143 cluster { 2144 name: %s 2145 listen: 127.0.0.1:%d 2146 routes = [%s] 2147 } 2148 2149 accounts: { 2150 JS: { 2151 jetstream: enabled 2152 users: [ {user: js, password: pwd} ] 2153 exports [ 2154 # This is streaming to a delivery subject for a push based consumer. 2155 { stream: "deliver.ORDERS.*" } 2156 # This is to ack received messages. This is a service to support sync ack. 2157 { service: "$JS.ACK.ORDERS.*.>" } 2158 # To support ordered consumers, flow control. 2159 { service: "$JS.FC.>" } 2160 ] 2161 }, 2162 IM: { 2163 users: [ {user: im, password: pwd} ] 2164 imports [ 2165 { stream: { account: JS, subject: "deliver.ORDERS.route" }} 2166 { stream: { account: JS, subject: "deliver.ORDERS.gateway" }} 2167 { stream: { account: JS, subject: "deliver.ORDERS.leaf1" }} 2168 { stream: { account: JS, subject: "deliver.ORDERS.leaf2" }} 2169 { service: {account: JS, subject: "$JS.FC.>" }} 2170 ] 2171 }, 2172 $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }, 2173 } 2174 leaf { 2175 listen: 127.0.0.1:-1 2176 }` 2177 2178 test := func(t *testing.T, queue bool) { 2179 c := createJetStreamSuperClusterWithTemplate(t, template, 3, 2) 2180 defer c.shutdown() 2181 2182 s := c.randomServer() 2183 nc, js := jsClientConnect(t, s, nats.UserInfo("js", "pwd")) 2184 defer nc.Close() 2185 2186 _, err := js.AddStream(&nats.StreamConfig{ 2187 Name: "ORDERS", 2188 Subjects: []string{"foo"}, // The JS subject. 2189 Replicas: 3, 2190 Placement: &nats.Placement{Cluster: "C1"}, 2191 }) 2192 require_NoError(t, err) 2193 2194 _, err = js.Publish("foo", []byte("OK")) 2195 require_NoError(t, err) 2196 2197 for dur, deliver := range map[string]string{ 2198 "dur-route": "deliver.ORDERS.route", 2199 "dur-gateway": "deliver.ORDERS.gateway", 2200 "dur-leaf-1": "deliver.ORDERS.leaf1", 2201 "dur-leaf-2": "deliver.ORDERS.leaf2", 2202 } { 2203 cfg := &nats.ConsumerConfig{ 2204 Durable: dur, 2205 DeliverSubject: deliver, 2206 AckPolicy: nats.AckExplicitPolicy, 2207 } 2208 if queue { 2209 cfg.DeliverGroup = "queue" 2210 } 2211 _, err = js.AddConsumer("ORDERS", cfg) 2212 require_NoError(t, err) 2213 } 2214 2215 testCase := func(t *testing.T, s *Server, dSubj string) { 2216 nc2, err := nats.Connect(s.ClientURL(), nats.UserInfo("im", "pwd")) 2217 require_NoError(t, err) 2218 defer nc2.Close() 2219 2220 var sub *nats.Subscription 2221 if queue { 2222 sub, err = nc2.QueueSubscribeSync(dSubj, "queue") 2223 } else { 2224 sub, err = nc2.SubscribeSync(dSubj) 2225 } 2226 require_NoError(t, err) 2227 2228 m, err := sub.NextMsg(time.Second) 2229 require_NoError(t, err) 2230 2231 if m.Subject != "foo" { 2232 t.Fatalf("Subject not mapped correctly across account boundary, expected %q got %q", "foo", m.Subject) 2233 } 2234 require_False(t, strings.Contains(m.Reply, "@")) 2235 } 2236 2237 t.Run("route", func(t *testing.T) { 2238 // pick random non consumer leader so we receive via route 2239 s := c.clusterForName("C1").randomNonConsumerLeader("JS", "ORDERS", "dur-route") 2240 testCase(t, s, "deliver.ORDERS.route") 2241 }) 2242 t.Run("gateway", func(t *testing.T) { 2243 // pick server with inbound gateway from consumer leader, so we receive from gateway and have no route in between 2244 scl := c.clusterForName("C1").consumerLeader("JS", "ORDERS", "dur-gateway") 2245 var sfound *Server 2246 for _, s := range c.clusterForName("C2").servers { 2247 s.mu.Lock() 2248 for _, c := range s.gateway.in { 2249 if c.GetName() == scl.info.ID { 2250 sfound = s 2251 break 2252 } 2253 } 2254 s.mu.Unlock() 2255 if sfound != nil { 2256 break 2257 } 2258 } 2259 testCase(t, sfound, "deliver.ORDERS.gateway") 2260 }) 2261 t.Run("leaf-post-export", func(t *testing.T) { 2262 // create leaf node server connected post export/import 2263 scl := c.clusterForName("C1").consumerLeader("JS", "ORDERS", "dur-leaf-1") 2264 cf := createConfFile(t, []byte(fmt.Sprintf(` 2265 port: -1 2266 leafnodes { 2267 remotes [ { url: "nats://im:pwd@127.0.0.1:%d" } ] 2268 } 2269 authorization: { 2270 user: im, 2271 password: pwd 2272 } 2273 `, scl.getOpts().LeafNode.Port))) 2274 s, _ := RunServerWithConfig(cf) 2275 defer s.Shutdown() 2276 checkLeafNodeConnected(t, scl) 2277 testCase(t, s, "deliver.ORDERS.leaf1") 2278 }) 2279 t.Run("leaf-pre-export", func(t *testing.T) { 2280 // create leaf node server connected pre export, perform export/import on leaf node server 2281 scl := c.clusterForName("C1").consumerLeader("JS", "ORDERS", "dur-leaf-2") 2282 cf := createConfFile(t, []byte(fmt.Sprintf(` 2283 port: -1 2284 leafnodes { 2285 remotes [ { url: "nats://js:pwd@127.0.0.1:%d", account: JS2 } ] 2286 } 2287 accounts: { 2288 JS2: { 2289 users: [ {user: js, password: pwd} ] 2290 exports [ 2291 # This is streaming to a delivery subject for a push based consumer. 2292 { stream: "deliver.ORDERS.leaf2" } 2293 # This is to ack received messages. This is a service to support sync ack. 2294 { service: "$JS.ACK.ORDERS.*.>" } 2295 # To support ordered consumers, flow control. 2296 { service: "$JS.FC.>" } 2297 ] 2298 }, 2299 IM2: { 2300 users: [ {user: im, password: pwd} ] 2301 imports [ 2302 { stream: { account: JS2, subject: "deliver.ORDERS.leaf2" }} 2303 { service: {account: JS2, subject: "$JS.FC.>" }} 2304 ] 2305 }, 2306 } 2307 `, scl.getOpts().LeafNode.Port))) 2308 s, _ := RunServerWithConfig(cf) 2309 defer s.Shutdown() 2310 checkLeafNodeConnected(t, scl) 2311 testCase(t, s, "deliver.ORDERS.leaf2") 2312 }) 2313 } 2314 2315 t.Run("noQueue", func(t *testing.T) { 2316 test(t, false) 2317 }) 2318 t.Run("queue", func(t *testing.T) { 2319 test(t, true) 2320 }) 2321 } 2322 2323 func TestJetStreamSuperClusterMaxHaAssets(t *testing.T) { 2324 sc := createJetStreamSuperClusterWithTemplateAndModHook(t, ` 2325 listen: 127.0.0.1:-1 2326 server_name: %s 2327 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s', limits: {max_ha_assets: 2}} 2328 cluster { 2329 name: %s 2330 listen: 127.0.0.1:%d 2331 routes = [%s] 2332 } 2333 accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } } 2334 `, 3, 2, 2335 func(serverName, clusterName, storeDir, conf string) string { 2336 return conf 2337 }, nil) 2338 defer sc.shutdown() 2339 2340 // speed up statsz reporting 2341 for _, c := range sc.clusters { 2342 for _, s := range c.servers { 2343 s.mu.Lock() 2344 s.sys.statsz = 10 * time.Millisecond 2345 s.sys.cstatsz = s.sys.statsz 2346 s.sys.stmr.Reset(s.sys.statsz) 2347 s.mu.Unlock() 2348 } 2349 } 2350 2351 nc, js := jsClientConnect(t, sc.randomServer()) 2352 defer nc.Close() 2353 2354 ncSys := natsConnect(t, sc.randomServer().ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 2355 defer ncSys.Close() 2356 statszSub, err := ncSys.SubscribeSync(fmt.Sprintf(serverStatsSubj, "*")) 2357 require_NoError(t, err) 2358 require_NoError(t, ncSys.Flush()) 2359 2360 waitStatsz := func(peers, haassets int) { 2361 t.Helper() 2362 for peersWithExactHaAssets := 0; peersWithExactHaAssets < peers; { 2363 m, err := statszSub.NextMsg(time.Second) 2364 require_NoError(t, err) 2365 var statsz ServerStatsMsg 2366 err = json.Unmarshal(m.Data, &statsz) 2367 require_NoError(t, err) 2368 if statsz.Stats.JetStream == nil { 2369 continue 2370 } 2371 if haassets == statsz.Stats.JetStream.Stats.HAAssets { 2372 peersWithExactHaAssets++ 2373 } 2374 } 2375 } 2376 waitStatsz(6, 1) // counts _meta_ 2377 _, err = js.AddStream(&nats.StreamConfig{Name: "S0", Replicas: 1, Placement: &nats.Placement{Cluster: "C1"}}) 2378 require_NoError(t, err) 2379 waitStatsz(6, 1) 2380 _, err = js.AddStream(&nats.StreamConfig{Name: "S1", Replicas: 3, Placement: &nats.Placement{Cluster: "C1"}}) 2381 require_NoError(t, err) 2382 waitStatsz(3, 2) 2383 waitStatsz(3, 1) 2384 _, err = js.AddStream(&nats.StreamConfig{Name: "S2", Replicas: 3, Placement: &nats.Placement{Cluster: "C1"}}) 2385 require_NoError(t, err) 2386 waitStatsz(3, 3) 2387 waitStatsz(3, 1) 2388 _, err = js.AddStream(&nats.StreamConfig{Name: "S3", Replicas: 3, Placement: &nats.Placement{Cluster: "C1"}}) 2389 require_Error(t, err) 2390 require_Contains(t, err.Error(), "nats: no suitable peers for placement") 2391 require_Contains(t, err.Error(), "miscellaneous issue") 2392 require_NoError(t, js.DeleteStream("S1")) 2393 waitStatsz(3, 2) 2394 waitStatsz(3, 1) 2395 _, err = js.AddConsumer("S2", &nats.ConsumerConfig{Durable: "DUR1", AckPolicy: nats.AckExplicitPolicy}) 2396 require_NoError(t, err) 2397 waitStatsz(3, 3) 2398 waitStatsz(3, 1) 2399 _, err = js.AddConsumer("S2", &nats.ConsumerConfig{Durable: "DUR2", AckPolicy: nats.AckExplicitPolicy}) 2400 require_Error(t, err) 2401 require_Equal(t, err.Error(), "nats: insufficient resources") 2402 _, err = js.AddConsumer("S2", &nats.ConsumerConfig{AckPolicy: nats.AckExplicitPolicy}) 2403 require_NoError(t, err) 2404 waitStatsz(3, 3) 2405 waitStatsz(3, 1) 2406 _, err = js.UpdateStream(&nats.StreamConfig{Name: "S2", Replicas: 3, Description: "foobar"}) 2407 require_NoError(t, err) 2408 waitStatsz(3, 3) 2409 waitStatsz(3, 1) 2410 si, err := js.AddStream(&nats.StreamConfig{Name: "S4", Replicas: 3}) 2411 require_NoError(t, err) 2412 require_Equal(t, si.Cluster.Name, "C2") 2413 waitStatsz(3, 3) 2414 waitStatsz(3, 2) 2415 si, err = js.AddStream(&nats.StreamConfig{Name: "S5", Replicas: 3}) 2416 require_NoError(t, err) 2417 require_Equal(t, si.Cluster.Name, "C2") 2418 waitStatsz(6, 3) 2419 _, err = js.AddConsumer("S4", &nats.ConsumerConfig{Durable: "DUR2", AckPolicy: nats.AckExplicitPolicy}) 2420 require_Error(t, err) 2421 require_Equal(t, err.Error(), "nats: insufficient resources") 2422 _, err = js.UpdateStream(&nats.StreamConfig{Name: "S2", Replicas: 3, Placement: &nats.Placement{Cluster: "C2"}}) 2423 require_Error(t, err) 2424 require_Contains(t, err.Error(), "nats: no suitable peers for placement") 2425 require_Contains(t, err.Error(), "miscellaneous issue") 2426 } 2427 2428 func TestJetStreamSuperClusterStreamAlternates(t *testing.T) { 2429 sc := createJetStreamTaggedSuperCluster(t) 2430 defer sc.shutdown() 2431 2432 nc, js := jsClientConnect(t, sc.randomServer()) 2433 defer nc.Close() 2434 2435 // C1 2436 _, err := js.AddStream(&nats.StreamConfig{ 2437 Name: "SOURCE", 2438 Subjects: []string{"foo", "bar", "baz"}, 2439 Replicas: 3, 2440 Placement: &nats.Placement{Tags: []string{"cloud:aws", "country:us"}}, 2441 }) 2442 require_NoError(t, err) 2443 2444 // C2 2445 _, err = js.AddStream(&nats.StreamConfig{ 2446 Name: "MIRROR-1", 2447 Replicas: 1, 2448 Mirror: &nats.StreamSource{Name: "SOURCE"}, 2449 Placement: &nats.Placement{Tags: []string{"cloud:gcp", "country:uk"}}, 2450 }) 2451 require_NoError(t, err) 2452 2453 // C3 2454 _, err = js.AddStream(&nats.StreamConfig{ 2455 Name: "MIRROR-2", 2456 Replicas: 2, 2457 Mirror: &nats.StreamSource{Name: "SOURCE"}, 2458 Placement: &nats.Placement{Tags: []string{"cloud:az", "country:jp"}}, 2459 }) 2460 require_NoError(t, err) 2461 2462 // No client support yet, so do by hand. 2463 getStreamInfo := func(nc *nats.Conn, expected string) { 2464 t.Helper() 2465 resp, err := nc.Request(fmt.Sprintf(JSApiStreamInfoT, "SOURCE"), nil, time.Second) 2466 require_NoError(t, err) 2467 var si StreamInfo 2468 err = json.Unmarshal(resp.Data, &si) 2469 require_NoError(t, err) 2470 require_True(t, len(si.Alternates) == 3) 2471 require_True(t, si.Alternates[0].Cluster == expected) 2472 seen := make(map[string]struct{}) 2473 for _, alt := range si.Alternates { 2474 seen[alt.Cluster] = struct{}{} 2475 } 2476 require_True(t, len(seen) == 3) 2477 } 2478 2479 // Connect to different clusters to check ordering. 2480 nc, _ = jsClientConnect(t, sc.clusterForName("C1").randomServer()) 2481 defer nc.Close() 2482 getStreamInfo(nc, "C1") 2483 nc, _ = jsClientConnect(t, sc.clusterForName("C2").randomServer()) 2484 defer nc.Close() 2485 getStreamInfo(nc, "C2") 2486 nc, _ = jsClientConnect(t, sc.clusterForName("C3").randomServer()) 2487 defer nc.Close() 2488 getStreamInfo(nc, "C3") 2489 } 2490 2491 // We had a scenario where a consumer would not recover properly on restart due to 2492 // the cluster state not being set properly when checking source subjects. 2493 func TestJetStreamSuperClusterStateOnRestartPreventsConsumerRecovery(t *testing.T) { 2494 sc := createJetStreamTaggedSuperCluster(t) 2495 defer sc.shutdown() 2496 2497 nc, js := jsClientConnect(t, sc.randomServer()) 2498 defer nc.Close() 2499 2500 // C1 2501 _, err := js.AddStream(&nats.StreamConfig{ 2502 Name: "SOURCE", 2503 Subjects: []string{"foo", "bar"}, 2504 Replicas: 3, 2505 Placement: &nats.Placement{Tags: []string{"cloud:aws", "country:us"}}, 2506 }) 2507 require_NoError(t, err) 2508 2509 // C2 2510 _, err = js.AddStream(&nats.StreamConfig{ 2511 Name: "DS", 2512 Subjects: []string{"baz"}, 2513 Replicas: 3, 2514 Sources: []*nats.StreamSource{{Name: "SOURCE"}}, 2515 Placement: &nats.Placement{Tags: []string{"cloud:gcp", "country:uk"}}, 2516 }) 2517 require_NoError(t, err) 2518 2519 // Bind to DS and match filter subject of SOURCE. 2520 _, err = js.AddConsumer("DS", &nats.ConsumerConfig{ 2521 Durable: "dlc", 2522 AckPolicy: nats.AckExplicitPolicy, 2523 FilterSubject: "foo", 2524 DeliverSubject: "d", 2525 }) 2526 require_NoError(t, err) 2527 2528 // Send a few messages. 2529 for i := 0; i < 100; i++ { 2530 _, err := js.Publish("foo", []byte("HELLO")) 2531 require_NoError(t, err) 2532 } 2533 sub := natsSubSync(t, nc, "d") 2534 natsNexMsg(t, sub, time.Second) 2535 2536 c := sc.clusterForName("C2") 2537 cl := c.consumerLeader("$G", "DS", "dlc") 2538 2539 // Pull source out from underneath the downstream stream. 2540 err = js.DeleteStream("SOURCE") 2541 require_NoError(t, err) 2542 2543 cl.Shutdown() 2544 cl = c.restartServer(cl) 2545 c.waitOnServerHealthz(cl) 2546 2547 // Now make sure the consumer is still on this server and has restarted properly. 2548 mset, err := cl.GlobalAccount().lookupStream("DS") 2549 require_NoError(t, err) 2550 if o := mset.lookupConsumer("dlc"); o == nil { 2551 t.Fatalf("Consumer was not properly restarted") 2552 } 2553 } 2554 2555 // We allow mirrors to opt-in to direct get in a distributed queue group. 2556 func TestJetStreamSuperClusterStreamDirectGetMirrorQueueGroup(t *testing.T) { 2557 sc := createJetStreamTaggedSuperCluster(t) 2558 defer sc.shutdown() 2559 2560 nc, js := jsClientConnect(t, sc.randomServer()) 2561 defer nc.Close() 2562 2563 // C1 2564 // Do by hand for now. 2565 cfg := &StreamConfig{ 2566 Name: "SOURCE", 2567 Subjects: []string{"kv.>"}, 2568 MaxMsgsPer: 1, 2569 Placement: &Placement{Tags: []string{"cloud:aws", "country:us"}}, 2570 AllowDirect: true, 2571 Replicas: 3, 2572 Storage: MemoryStorage, 2573 } 2574 addStream(t, nc, cfg) 2575 2576 num := 100 2577 for i := 0; i < num; i++ { 2578 js.PublishAsync(fmt.Sprintf("kv.%d", i), []byte("VAL")) 2579 } 2580 select { 2581 case <-js.PublishAsyncComplete(): 2582 case <-time.After(5 * time.Second): 2583 t.Fatalf("Did not receive completion signal") 2584 } 2585 2586 // C2 2587 cfg = &StreamConfig{ 2588 Name: "M1", 2589 Mirror: &StreamSource{Name: "SOURCE"}, 2590 Placement: &Placement{Tags: []string{"cloud:gcp", "country:uk"}}, 2591 MirrorDirect: true, 2592 Storage: MemoryStorage, 2593 } 2594 addStream(t, nc, cfg) 2595 2596 // C3 (clustered) 2597 cfg = &StreamConfig{ 2598 Name: "M2", 2599 Mirror: &StreamSource{Name: "SOURCE"}, 2600 Replicas: 3, 2601 Placement: &Placement{Tags: []string{"country:jp"}}, 2602 MirrorDirect: true, 2603 Storage: MemoryStorage, 2604 } 2605 addStream(t, nc, cfg) 2606 2607 checkFor(t, 5*time.Second, 100*time.Millisecond, func() error { 2608 si, err := js.StreamInfo("M2") 2609 require_NoError(t, err) 2610 if si.State.Msgs != uint64(num) { 2611 return fmt.Errorf("Expected %d msgs, got state: %d", num, si.State.Msgs) 2612 } 2613 return nil 2614 }) 2615 2616 // Since last one was an R3, check and wait for the direct subscription. 2617 checkFor(t, 2*time.Second, 100*time.Millisecond, func() error { 2618 sl := sc.clusterForName("C3").streamLeader("$G", "M2") 2619 if mset, err := sl.GlobalAccount().lookupStream("M2"); err == nil { 2620 mset.mu.RLock() 2621 ok := mset.mirror.dsub != nil 2622 mset.mu.RUnlock() 2623 if ok { 2624 return nil 2625 } 2626 } 2627 return fmt.Errorf("No dsub yet") 2628 }) 2629 2630 // Always do a direct get to the source, but check that we are getting answers from the mirrors when connected to their cluster. 2631 getSubj := fmt.Sprintf(JSDirectMsgGetT, "SOURCE") 2632 req := []byte(`{"last_by_subj":"kv.22"}`) 2633 getMsg := func(c *nats.Conn) *nats.Msg { 2634 m, err := c.Request(getSubj, req, time.Second) 2635 require_NoError(t, err) 2636 require_True(t, string(m.Data) == "VAL") 2637 require_True(t, m.Header.Get(JSSequence) == "23") 2638 require_True(t, m.Header.Get(JSSubject) == "kv.22") 2639 return m 2640 } 2641 2642 // C1 -> SOURCE 2643 nc, _ = jsClientConnect(t, sc.clusterForName("C1").randomServer()) 2644 defer nc.Close() 2645 2646 m := getMsg(nc) 2647 require_True(t, m.Header.Get(JSStream) == "SOURCE") 2648 2649 // C2 -> M1 2650 nc, _ = jsClientConnect(t, sc.clusterForName("C2").randomServer()) 2651 defer nc.Close() 2652 2653 m = getMsg(nc) 2654 require_True(t, m.Header.Get(JSStream) == "M1") 2655 2656 // C3 -> M2 2657 nc, _ = jsClientConnect(t, sc.clusterForName("C3").randomServer()) 2658 defer nc.Close() 2659 2660 m = getMsg(nc) 2661 require_True(t, m.Header.Get(JSStream) == "M2") 2662 } 2663 2664 func TestJetStreamSuperClusterTagInducedMoveCancel(t *testing.T) { 2665 server := map[string]struct{}{} 2666 sc := createJetStreamSuperClusterWithTemplateAndModHook(t, jsClusterTempl, 4, 2, 2667 func(serverName, clusterName, storeDir, conf string) string { 2668 server[serverName] = struct{}{} 2669 return fmt.Sprintf("%s\nserver_tags: [%s]", conf, clusterName) 2670 }, nil) 2671 defer sc.shutdown() 2672 2673 // Client based API 2674 c := sc.randomCluster() 2675 srv := c.randomNonLeader() 2676 nc, js := jsClientConnect(t, srv) 2677 defer nc.Close() 2678 2679 cfg := &nats.StreamConfig{ 2680 Name: "TEST", 2681 Subjects: []string{"foo"}, 2682 Placement: &nats.Placement{Tags: []string{"C1"}}, 2683 Replicas: 3, 2684 } 2685 siCreate, err := js.AddStream(cfg) 2686 require_NoError(t, err) 2687 require_Equal(t, siCreate.Cluster.Name, "C1") 2688 2689 toSend := uint64(1_000) 2690 for i := uint64(0); i < toSend; i++ { 2691 _, err = js.Publish("foo", nil) 2692 require_NoError(t, err) 2693 } 2694 2695 ncsys, err := nats.Connect(srv.ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 2696 require_NoError(t, err) 2697 defer ncsys.Close() 2698 2699 // cause a move by altering placement tags 2700 cfg.Placement.Tags = []string{"C2"} 2701 _, err = js.UpdateStream(cfg) 2702 require_NoError(t, err) 2703 2704 rmsg, err := ncsys.Request(fmt.Sprintf(JSApiServerStreamCancelMoveT, "$G", "TEST"), nil, 5*time.Second) 2705 require_NoError(t, err) 2706 var cancelResp JSApiStreamUpdateResponse 2707 require_NoError(t, json.Unmarshal(rmsg.Data, &cancelResp)) 2708 if cancelResp.Error != nil && ErrorIdentifier(cancelResp.Error.ErrCode) == JSStreamMoveNotInProgress { 2709 t.Skip("This can happen with delays, when Move completed before Cancel", cancelResp.Error) 2710 return 2711 } 2712 require_True(t, cancelResp.Error == nil) 2713 2714 checkFor(t, 10*time.Second, 250*time.Millisecond, func() error { 2715 si, err := js.StreamInfo("TEST") 2716 require_NoError(t, err) 2717 if si.Config.Placement != nil { 2718 return fmt.Errorf("expected placement to be cleared got: %+v", si.Config.Placement) 2719 } 2720 return nil 2721 }) 2722 } 2723 2724 func TestJetStreamSuperClusterMoveCancel(t *testing.T) { 2725 usageTickOld := usageTick 2726 usageTick = 250 * time.Millisecond 2727 defer func() { 2728 usageTick = usageTickOld 2729 }() 2730 2731 server := map[string]struct{}{} 2732 sc := createJetStreamSuperClusterWithTemplateAndModHook(t, jsClusterTempl, 4, 2, 2733 func(serverName, clusterName, storeDir, conf string) string { 2734 server[serverName] = struct{}{} 2735 return fmt.Sprintf("%s\nserver_tags: [%s]", conf, serverName) 2736 }, nil) 2737 defer sc.shutdown() 2738 2739 // Client based API 2740 c := sc.randomCluster() 2741 srv := c.randomNonLeader() 2742 nc, js := jsClientConnect(t, srv) 2743 defer nc.Close() 2744 2745 siCreate, err := js.AddStream(&nats.StreamConfig{ 2746 Name: "TEST", 2747 Subjects: []string{"foo"}, 2748 Replicas: 3, 2749 }) 2750 require_NoError(t, err) 2751 streamPeerSrv := []string{siCreate.Cluster.Leader, siCreate.Cluster.Replicas[0].Name, siCreate.Cluster.Replicas[1].Name} 2752 // determine empty server 2753 for _, s := range streamPeerSrv { 2754 delete(server, s) 2755 } 2756 // pick left over server in same cluster as other server 2757 emptySrv := _EMPTY_ 2758 for s := range server { 2759 // server name is prefixed with cluster name 2760 if strings.HasPrefix(s, c.name) { 2761 emptySrv = s 2762 break 2763 } 2764 } 2765 2766 expectedPeers := map[string]struct{}{ 2767 getHash(streamPeerSrv[0]): {}, 2768 getHash(streamPeerSrv[1]): {}, 2769 getHash(streamPeerSrv[2]): {}, 2770 } 2771 2772 _, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "DUR", AckPolicy: nats.AckExplicitPolicy}) 2773 require_NoError(t, err) 2774 ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{InactiveThreshold: time.Hour, AckPolicy: nats.AckExplicitPolicy}) 2775 require_NoError(t, err) 2776 ephName := ci.Name 2777 2778 toSend := uint64(1_000) 2779 for i := uint64(0); i < toSend; i++ { 2780 _, err = js.Publish("foo", nil) 2781 require_NoError(t, err) 2782 } 2783 2784 serverEmpty := func(fromSrv string) error { 2785 if jszAfter, err := c.serverByName(fromSrv).Jsz(nil); err != nil { 2786 return fmt.Errorf("could not fetch JS info for server: %v", err) 2787 } else if jszAfter.Streams != 0 { 2788 return fmt.Errorf("empty server still has %d streams", jszAfter.Streams) 2789 } else if jszAfter.Consumers != 0 { 2790 return fmt.Errorf("empty server still has %d consumers", jszAfter.Consumers) 2791 } else if jszAfter.Bytes != 0 { 2792 return fmt.Errorf("empty server still has %d storage", jszAfter.Store) 2793 } 2794 return nil 2795 } 2796 2797 checkSrvInvariant := func(s *Server, expectedPeers map[string]struct{}) error { 2798 js, cc := s.getJetStreamCluster() 2799 js.mu.Lock() 2800 defer js.mu.Unlock() 2801 if sa, ok := cc.streams["$G"]["TEST"]; !ok { 2802 return fmt.Errorf("stream not found") 2803 } else if len(sa.Group.Peers) != len(expectedPeers) { 2804 return fmt.Errorf("stream peer group size not %d, but %d", len(expectedPeers), len(sa.Group.Peers)) 2805 } else if da, ok := sa.consumers["DUR"]; !ok { 2806 return fmt.Errorf("durable not found") 2807 } else if len(da.Group.Peers) != len(expectedPeers) { 2808 return fmt.Errorf("durable peer group size not %d, but %d", len(expectedPeers), len(da.Group.Peers)) 2809 } else if ea, ok := sa.consumers[ephName]; !ok { 2810 return fmt.Errorf("ephemeral not found") 2811 } else if len(ea.Group.Peers) != 1 { 2812 return fmt.Errorf("ephemeral peer group size not 1, but %d", len(ea.Group.Peers)) 2813 } else if _, ok := expectedPeers[ea.Group.Peers[0]]; !ok { 2814 return fmt.Errorf("ephemeral peer not an expected peer") 2815 } else { 2816 for _, p := range sa.Group.Peers { 2817 if _, ok := expectedPeers[p]; !ok { 2818 return fmt.Errorf("peer not expected") 2819 } 2820 found := false 2821 for _, dp := range da.Group.Peers { 2822 if p == dp { 2823 found = true 2824 break 2825 } 2826 } 2827 if !found { 2828 t.Logf("durable peer group does not match stream peer group") 2829 } 2830 } 2831 } 2832 return nil 2833 } 2834 2835 ncsys, err := nats.Connect(srv.ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 2836 require_NoError(t, err) 2837 defer ncsys.Close() 2838 2839 time.Sleep(2 * usageTick) 2840 aiBefore, err := js.AccountInfo() 2841 require_NoError(t, err) 2842 2843 for _, moveFromSrv := range streamPeerSrv { 2844 moveReq, err := json.Marshal(&JSApiMetaServerStreamMoveRequest{Server: moveFromSrv, Tags: []string{emptySrv}}) 2845 require_NoError(t, err) 2846 rmsg, err := ncsys.Request(fmt.Sprintf(JSApiServerStreamMoveT, "$G", "TEST"), moveReq, 5*time.Second) 2847 require_NoError(t, err) 2848 var moveResp JSApiStreamUpdateResponse 2849 require_NoError(t, json.Unmarshal(rmsg.Data, &moveResp)) 2850 require_True(t, moveResp.Error == nil) 2851 2852 rmsg, err = ncsys.Request(fmt.Sprintf(JSApiServerStreamCancelMoveT, "$G", "TEST"), nil, 5*time.Second) 2853 require_NoError(t, err) 2854 var cancelResp JSApiStreamUpdateResponse 2855 require_NoError(t, json.Unmarshal(rmsg.Data, &cancelResp)) 2856 if cancelResp.Error != nil && ErrorIdentifier(cancelResp.Error.ErrCode) == JSStreamMoveNotInProgress { 2857 t.Skip("This can happen with delays, when Move completed before Cancel", cancelResp.Error) 2858 return 2859 } 2860 require_True(t, cancelResp.Error == nil) 2861 2862 for _, sExpected := range streamPeerSrv { 2863 s := c.serverByName(sExpected) 2864 require_True(t, s.JetStreamIsStreamAssigned("$G", "TEST")) 2865 checkFor(t, 20*time.Second, 100*time.Millisecond, func() error { return checkSrvInvariant(s, expectedPeers) }) 2866 } 2867 checkFor(t, 10*time.Second, 100*time.Millisecond, func() error { return serverEmpty(emptySrv) }) 2868 checkFor(t, 3*usageTick, 100*time.Millisecond, func() error { 2869 if aiAfter, err := js.AccountInfo(); err != nil { 2870 return err 2871 } else if aiAfter.Store != aiBefore.Store { 2872 return fmt.Errorf("store before %d and after %d don't match", aiBefore.Store, aiAfter.Store) 2873 } else { 2874 return nil 2875 } 2876 }) 2877 } 2878 } 2879 2880 func TestJetStreamSuperClusterDoubleStreamMove(t *testing.T) { 2881 server := map[string]struct{}{} 2882 sc := createJetStreamSuperClusterWithTemplateAndModHook(t, jsClusterTempl, 4, 2, 2883 func(serverName, clusterName, storeDir, conf string) string { 2884 server[serverName] = struct{}{} 2885 return fmt.Sprintf("%s\nserver_tags: [%s]", conf, serverName) 2886 }, nil) 2887 defer sc.shutdown() 2888 2889 // Client based API 2890 c := sc.randomCluster() 2891 srv := c.randomNonLeader() 2892 nc, js := jsClientConnect(t, srv) 2893 defer nc.Close() 2894 2895 siCreate, err := js.AddStream(&nats.StreamConfig{ 2896 Name: "TEST", 2897 Subjects: []string{"foo"}, 2898 Replicas: 3, 2899 }) 2900 require_NoError(t, err) 2901 srvMoveList := []string{siCreate.Cluster.Leader, siCreate.Cluster.Replicas[0].Name, siCreate.Cluster.Replicas[1].Name} 2902 // determine empty server 2903 for _, s := range srvMoveList { 2904 delete(server, s) 2905 } 2906 // pick left over server in same cluster as other server 2907 for s := range server { 2908 // server name is prefixed with cluster name 2909 if strings.HasPrefix(s, c.name) { 2910 srvMoveList = append(srvMoveList, s) 2911 break 2912 } 2913 } 2914 2915 servers := []*Server{ 2916 c.serverByName(srvMoveList[0]), 2917 c.serverByName(srvMoveList[1]), 2918 c.serverByName(srvMoveList[2]), 2919 c.serverByName(srvMoveList[3]), // starts out empty 2920 } 2921 2922 _, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "DUR", AckPolicy: nats.AckExplicitPolicy}) 2923 require_NoError(t, err) 2924 ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{InactiveThreshold: time.Hour, AckPolicy: nats.AckExplicitPolicy}) 2925 require_NoError(t, err) 2926 ephName := ci.Name 2927 2928 toSend := uint64(100) 2929 for i := uint64(0); i < toSend; i++ { 2930 _, err = js.Publish("foo", nil) 2931 require_NoError(t, err) 2932 } 2933 2934 ncsys, err := nats.Connect(srv.ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 2935 require_NoError(t, err) 2936 defer ncsys.Close() 2937 2938 move := func(fromSrv string, toTags ...string) { 2939 sEmpty := c.serverByName(fromSrv) 2940 jszBefore, err := sEmpty.Jsz(nil) 2941 require_NoError(t, err) 2942 require_True(t, jszBefore.Streams == 1) 2943 2944 moveReq, err := json.Marshal(&JSApiMetaServerStreamMoveRequest{ 2945 Server: fromSrv, Tags: toTags}) 2946 require_NoError(t, err) 2947 rmsg, err := ncsys.Request(fmt.Sprintf(JSApiServerStreamMoveT, "$G", "TEST"), moveReq, 100*time.Second) 2948 require_NoError(t, err) 2949 var moveResp JSApiStreamUpdateResponse 2950 require_NoError(t, json.Unmarshal(rmsg.Data, &moveResp)) 2951 require_True(t, moveResp.Error == nil) 2952 } 2953 2954 serverEmpty := func(fromSrv string) error { 2955 if jszAfter, err := c.serverByName(fromSrv).Jsz(nil); err != nil { 2956 return fmt.Errorf("could not fetch JS info for server: %v", err) 2957 } else if jszAfter.Streams != 0 { 2958 return fmt.Errorf("empty server still has %d streams", jszAfter.Streams) 2959 } else if jszAfter.Consumers != 0 { 2960 return fmt.Errorf("empty server still has %d consumers", jszAfter.Consumers) 2961 } else if jszAfter.Store != 0 { 2962 return fmt.Errorf("empty server still has %d storage", jszAfter.Store) 2963 } 2964 return nil 2965 } 2966 2967 moveComplete := func(toSrv string, expectedSet ...string) error { 2968 eSet := map[string]int{} 2969 foundInExpected := false 2970 for i, sExpected := range expectedSet { 2971 eSet[sExpected] = i 2972 s := c.serverByName(sExpected) 2973 if !s.JetStreamIsStreamAssigned("$G", "TEST") { 2974 return fmt.Errorf("expected stream to be assigned to %s", sExpected) 2975 } 2976 // test list order invariant 2977 js, cc := s.getJetStreamCluster() 2978 sExpHash := getHash(sExpected) 2979 js.mu.Lock() 2980 if sa, ok := cc.streams["$G"]["TEST"]; !ok { 2981 js.mu.Unlock() 2982 return fmt.Errorf("stream not found in cluster") 2983 } else if len(sa.Group.Peers) != 3 { 2984 js.mu.Unlock() 2985 return fmt.Errorf("peers not reset") 2986 } else if sa.Group.Peers[i] != sExpHash { 2987 js.mu.Unlock() 2988 return fmt.Errorf("stream: expected peer %s on index %d, got %s/%s", 2989 sa.Group.Peers[i], i, sExpHash, sExpected) 2990 } else if ca, ok := sa.consumers["DUR"]; !ok { 2991 js.mu.Unlock() 2992 return fmt.Errorf("durable not found in stream") 2993 } else { 2994 found := false 2995 for _, peer := range ca.Group.Peers { 2996 if peer == sExpHash { 2997 found = true 2998 break 2999 } 3000 } 3001 if !found { 3002 js.mu.Unlock() 3003 return fmt.Errorf("consumer expected peer %s/%s bud didn't find in %+v", 3004 sExpHash, sExpected, ca.Group.Peers) 3005 } 3006 if ephA, ok := sa.consumers[ephName]; ok { 3007 if len(ephA.Group.Peers) != 1 { 3008 return fmt.Errorf("ephemeral peers not reset") 3009 } 3010 foundInExpected = foundInExpected || (ephA.Group.Peers[0] == cc.meta.ID()) 3011 } 3012 } 3013 js.mu.Unlock() 3014 } 3015 if len(expectedSet) > 0 && !foundInExpected { 3016 return fmt.Errorf("ephemeral peer not expected") 3017 } 3018 for _, s := range servers { 3019 if jszAfter, err := c.serverByName(toSrv).Jsz(nil); err != nil { 3020 return fmt.Errorf("could not fetch JS info for server: %v", err) 3021 } else if jszAfter.Messages != toSend { 3022 return fmt.Errorf("messages not yet copied, got %d, expected %d", jszAfter.Messages, toSend) 3023 } 3024 nc, js := jsClientConnect(t, s) 3025 defer nc.Close() 3026 if si, err := js.StreamInfo("TEST", nats.MaxWait(time.Second)); err != nil { 3027 return fmt.Errorf("could not fetch stream info: %v", err) 3028 } else if len(si.Cluster.Replicas)+1 != si.Config.Replicas { 3029 return fmt.Errorf("not yet downsized replica should be empty has: %d %s", 3030 len(si.Cluster.Replicas), si.Cluster.Leader) 3031 } else if si.Cluster.Leader == _EMPTY_ { 3032 return fmt.Errorf("leader not found") 3033 } else if len(expectedSet) > 0 { 3034 if _, ok := eSet[si.Cluster.Leader]; !ok { 3035 return fmt.Errorf("leader %s not in expected set %+v", si.Cluster.Leader, eSet) 3036 } else if _, ok := eSet[si.Cluster.Replicas[0].Name]; !ok { 3037 return fmt.Errorf("leader %s not in expected set %+v", si.Cluster.Replicas[0].Name, eSet) 3038 } else if _, ok := eSet[si.Cluster.Replicas[1].Name]; !ok { 3039 return fmt.Errorf("leader %s not in expected set %+v", si.Cluster.Replicas[1].Name, eSet) 3040 } 3041 } 3042 nc.Close() 3043 } 3044 return nil 3045 } 3046 3047 moveAndCheck := func(from, to string, expectedSet ...string) { 3048 t.Helper() 3049 move(from, to) 3050 checkFor(t, 40*time.Second, 100*time.Millisecond, func() error { return moveComplete(to, expectedSet...) }) 3051 checkFor(t, 20*time.Second, 100*time.Millisecond, func() error { return serverEmpty(from) }) 3052 } 3053 3054 checkFor(t, 20*time.Second, 1000*time.Millisecond, func() error { return serverEmpty(srvMoveList[3]) }) 3055 // first iteration establishes order of server 0-2 (the internal order in the server could be 1,0,2) 3056 moveAndCheck(srvMoveList[0], srvMoveList[3]) 3057 moveAndCheck(srvMoveList[1], srvMoveList[0]) 3058 moveAndCheck(srvMoveList[2], srvMoveList[1]) 3059 moveAndCheck(srvMoveList[3], srvMoveList[2], srvMoveList[0], srvMoveList[1], srvMoveList[2]) 3060 // second iteration iterates in order 3061 moveAndCheck(srvMoveList[0], srvMoveList[3], srvMoveList[1], srvMoveList[2], srvMoveList[3]) 3062 moveAndCheck(srvMoveList[1], srvMoveList[0], srvMoveList[2], srvMoveList[3], srvMoveList[0]) 3063 moveAndCheck(srvMoveList[2], srvMoveList[1], srvMoveList[3], srvMoveList[0], srvMoveList[1]) 3064 moveAndCheck(srvMoveList[3], srvMoveList[2], srvMoveList[0], srvMoveList[1], srvMoveList[2]) 3065 // iterate in the opposite direction and establish order 2-0 3066 moveAndCheck(srvMoveList[2], srvMoveList[3], srvMoveList[0], srvMoveList[1], srvMoveList[3]) 3067 moveAndCheck(srvMoveList[1], srvMoveList[2], srvMoveList[0], srvMoveList[3], srvMoveList[2]) 3068 moveAndCheck(srvMoveList[0], srvMoveList[1], srvMoveList[3], srvMoveList[2], srvMoveList[1]) 3069 moveAndCheck(srvMoveList[3], srvMoveList[0], srvMoveList[2], srvMoveList[1], srvMoveList[0]) 3070 // move server in the middle of list 3071 moveAndCheck(srvMoveList[1], srvMoveList[3], srvMoveList[2], srvMoveList[0], srvMoveList[3]) 3072 moveAndCheck(srvMoveList[0], srvMoveList[1], srvMoveList[2], srvMoveList[3], srvMoveList[1]) 3073 moveAndCheck(srvMoveList[3], srvMoveList[0], srvMoveList[2], srvMoveList[1], srvMoveList[0]) 3074 // repeatedly use end 3075 moveAndCheck(srvMoveList[0], srvMoveList[3], srvMoveList[2], srvMoveList[1], srvMoveList[3]) 3076 moveAndCheck(srvMoveList[3], srvMoveList[0], srvMoveList[2], srvMoveList[1], srvMoveList[0]) 3077 moveAndCheck(srvMoveList[0], srvMoveList[3], srvMoveList[2], srvMoveList[1], srvMoveList[3]) 3078 moveAndCheck(srvMoveList[3], srvMoveList[0], srvMoveList[2], srvMoveList[1], srvMoveList[0]) 3079 } 3080 3081 func TestJetStreamSuperClusterPeerEvacuationAndStreamReassignment(t *testing.T) { 3082 s := createJetStreamSuperClusterWithTemplateAndModHook(t, jsClusterTempl, 4, 2, 3083 func(serverName, clusterName, storeDir, conf string) string { 3084 return fmt.Sprintf("%s\nserver_tags: [cluster:%s, server:%s]", conf, clusterName, serverName) 3085 }, nil) 3086 defer s.shutdown() 3087 3088 c := s.clusterForName("C1") 3089 3090 // Client based API 3091 srv := c.randomNonLeader() 3092 nc, js := jsClientConnect(t, srv) 3093 defer nc.Close() 3094 3095 test := func(t *testing.T, r int, moveTags []string, targetCluster string, testMigrateTo bool, listFrom bool) { 3096 si, err := js.AddStream(&nats.StreamConfig{ 3097 Name: "TEST", 3098 Subjects: []string{"foo"}, 3099 Replicas: r, 3100 }) 3101 require_NoError(t, err) 3102 defer js.DeleteStream("TEST") 3103 startSet := map[string]struct{}{ 3104 si.Cluster.Leader: {}, 3105 } 3106 for _, p := range si.Cluster.Replicas { 3107 startSet[p.Name] = struct{}{} 3108 } 3109 3110 _, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "DUR", AckPolicy: nats.AckExplicitPolicy}) 3111 require_NoError(t, err) 3112 3113 sub, err := js.SubscribeSync("foo") 3114 require_NoError(t, err) 3115 3116 for i := 0; i < 100; i++ { 3117 _, err = js.Publish("foo", nil) 3118 require_NoError(t, err) 3119 } 3120 3121 toMoveFrom := si.Cluster.Leader 3122 if !listFrom { 3123 toMoveFrom = _EMPTY_ 3124 } 3125 sLdr := c.serverByName(si.Cluster.Leader) 3126 jszBefore, err := sLdr.Jsz(nil) 3127 require_NoError(t, err) 3128 require_True(t, jszBefore.Streams == 1) 3129 require_True(t, jszBefore.Consumers >= 1) 3130 require_True(t, jszBefore.Store != 0) 3131 3132 migrateToServer := _EMPTY_ 3133 if testMigrateTo { 3134 // find an empty server 3135 for _, s := range c.servers { 3136 name := s.Name() 3137 found := si.Cluster.Leader == name 3138 if !found { 3139 for _, r := range si.Cluster.Replicas { 3140 if r.Name == name { 3141 found = true 3142 break 3143 } 3144 } 3145 } 3146 if !found { 3147 migrateToServer = name 3148 break 3149 } 3150 } 3151 jszAfter, err := c.serverByName(migrateToServer).Jsz(nil) 3152 require_NoError(t, err) 3153 require_True(t, jszAfter.Streams == 0) 3154 3155 moveTags = append(moveTags, fmt.Sprintf("server:%s", migrateToServer)) 3156 } 3157 3158 ncsys, err := nats.Connect(srv.ClientURL(), nats.UserInfo("admin", "s3cr3t!")) 3159 require_NoError(t, err) 3160 defer ncsys.Close() 3161 3162 moveReq, err := json.Marshal(&JSApiMetaServerStreamMoveRequest{Server: toMoveFrom, Tags: moveTags}) 3163 require_NoError(t, err) 3164 rmsg, err := ncsys.Request(fmt.Sprintf(JSApiServerStreamMoveT, "$G", "TEST"), moveReq, 100*time.Second) 3165 require_NoError(t, err) 3166 var moveResp JSApiStreamUpdateResponse 3167 require_NoError(t, json.Unmarshal(rmsg.Data, &moveResp)) 3168 require_True(t, moveResp.Error == nil) 3169 3170 // test move to particular server 3171 if testMigrateTo { 3172 toSrv := c.serverByName(migrateToServer) 3173 checkFor(t, 20*time.Second, 1000*time.Millisecond, func() error { 3174 jszAfter, err := toSrv.Jsz(nil) 3175 if err != nil { 3176 return fmt.Errorf("could not fetch JS info for server: %v", err) 3177 } 3178 if jszAfter.Streams != 1 { 3179 return fmt.Errorf("server expected to have one stream, has %d", jszAfter.Streams) 3180 } 3181 return nil 3182 }) 3183 } 3184 // Now wait until the stream is now current. 3185 checkFor(t, 20*time.Second, 100*time.Millisecond, func() error { 3186 si, err := js.StreamInfo("TEST", nats.MaxWait(time.Second)) 3187 if err != nil { 3188 return fmt.Errorf("could not fetch stream info: %v", err) 3189 } 3190 if si.Cluster.Leader == toMoveFrom { 3191 return fmt.Errorf("peer not removed yet: %+v", toMoveFrom) 3192 } 3193 if si.Cluster.Leader == _EMPTY_ { 3194 return fmt.Errorf("no leader yet") 3195 } 3196 if len(si.Cluster.Replicas) != r-1 { 3197 return fmt.Errorf("not yet downsized replica should be %d has: %d", r-1, len(si.Cluster.Replicas)) 3198 } 3199 if si.Config.Replicas != r { 3200 return fmt.Errorf("bad replica count %d", si.Config.Replicas) 3201 } 3202 if si.Cluster.Name != targetCluster { 3203 return fmt.Errorf("stream expected in %s but found in %s", si.Cluster.Name, targetCluster) 3204 } 3205 sNew := s.serverByName(si.Cluster.Leader) 3206 if jszNew, err := sNew.Jsz(nil); err != nil { 3207 return err 3208 } else if jszNew.Streams != 1 { 3209 return fmt.Errorf("new leader has %d streams, not one", jszNew.Streams) 3210 } else if jszNew.Store != jszBefore.Store { 3211 return fmt.Errorf("new leader has %d storage, should have %d", jszNew.Store, jszBefore.Store) 3212 } 3213 return nil 3214 }) 3215 // test draining 3216 checkFor(t, 20*time.Second, time.Second, func() error { 3217 if !listFrom { 3218 // when needed determine which server move moved away from 3219 si, err := js.StreamInfo("TEST", nats.MaxWait(time.Second)) 3220 if err != nil { 3221 return fmt.Errorf("could not fetch stream info: %v", err) 3222 } 3223 for n := range startSet { 3224 if n != si.Cluster.Leader { 3225 var found bool 3226 for _, p := range si.Cluster.Replicas { 3227 if p.Name == n { 3228 found = true 3229 break 3230 } 3231 } 3232 if !found { 3233 toMoveFrom = n 3234 } 3235 } 3236 } 3237 } 3238 if toMoveFrom == _EMPTY_ { 3239 return fmt.Errorf("server to move away from not found") 3240 } 3241 sEmpty := c.serverByName(toMoveFrom) 3242 jszAfter, err := sEmpty.Jsz(nil) 3243 if err != nil { 3244 return fmt.Errorf("could not fetch JS info for server: %v", err) 3245 } 3246 if jszAfter.Streams != 0 { 3247 return fmt.Errorf("empty server still has %d streams", jszAfter.Streams) 3248 } 3249 if jszAfter.Consumers != 0 { 3250 return fmt.Errorf("empty server still has %d consumers", jszAfter.Consumers) 3251 } 3252 if jszAfter.Store != 0 { 3253 return fmt.Errorf("empty server still has %d storage", jszAfter.Store) 3254 } 3255 return nil 3256 }) 3257 // consume messages from ephemeral consumer 3258 for i := 0; i < 100; i++ { 3259 _, err := sub.NextMsg(time.Second) 3260 require_NoError(t, err) 3261 } 3262 } 3263 3264 for i := 1; i <= 3; i++ { 3265 t.Run(fmt.Sprintf("r%d", i), func(t *testing.T) { 3266 test(t, i, nil, "C1", false, true) 3267 }) 3268 t.Run(fmt.Sprintf("r%d-explicit", i), func(t *testing.T) { 3269 test(t, i, nil, "C1", true, true) 3270 }) 3271 t.Run(fmt.Sprintf("r%d-nosrc", i), func(t *testing.T) { 3272 test(t, i, nil, "C1", false, false) 3273 }) 3274 } 3275 3276 t.Run("r3-cluster-move", func(t *testing.T) { 3277 test(t, 3, []string{"cluster:C2"}, "C2", false, false) 3278 }) 3279 t.Run("r3-cluster-move-nosrc", func(t *testing.T) { 3280 test(t, 3, []string{"cluster:C2"}, "C2", false, true) 3281 }) 3282 } 3283 3284 func TestJetStreamSuperClusterMirrorInheritsAllowDirect(t *testing.T) { 3285 sc := createJetStreamTaggedSuperCluster(t) 3286 defer sc.shutdown() 3287 3288 nc, js := jsClientConnect(t, sc.randomServer()) 3289 defer nc.Close() 3290 3291 _, err := js.AddStream(&nats.StreamConfig{ 3292 Name: "KV", 3293 Subjects: []string{"key.*"}, 3294 Placement: &nats.Placement{Tags: []string{"cloud:aws", "country:us"}}, 3295 MaxMsgsPerSubject: 1, 3296 AllowDirect: true, 3297 }) 3298 require_NoError(t, err) 3299 3300 _, err = js.AddStream(&nats.StreamConfig{ 3301 Name: "M", 3302 Mirror: &nats.StreamSource{Name: "KV"}, 3303 Placement: &nats.Placement{Tags: []string{"cloud:gcp", "country:uk"}}, 3304 }) 3305 require_NoError(t, err) 3306 3307 // Do direct grab for now. 3308 resp, err := nc.Request(fmt.Sprintf(JSApiStreamInfoT, "M"), nil, time.Second) 3309 require_NoError(t, err) 3310 var si StreamInfo 3311 err = json.Unmarshal(resp.Data, &si) 3312 require_NoError(t, err) 3313 3314 if !si.Config.MirrorDirect { 3315 t.Fatalf("Expected MirrorDirect to be inherited as true") 3316 } 3317 } 3318 3319 func TestJetStreamSuperClusterSystemLimitsPlacement(t *testing.T) { 3320 const largeSystemLimit = 1024 3321 const smallSystemLimit = 512 3322 3323 tmpl := ` 3324 listen: 127.0.0.1:-1 3325 server_name: %s 3326 jetstream: { 3327 max_mem_store: _MAXMEM_ 3328 max_file_store: _MAXFILE_ 3329 store_dir: '%s', 3330 } 3331 server_tags: [ 3332 _TAG_ 3333 ] 3334 leaf { 3335 listen: 127.0.0.1:-1 3336 } 3337 cluster { 3338 name: %s 3339 listen: 127.0.0.1:%d 3340 routes = [%s] 3341 } 3342 3343 accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } } 3344 ` 3345 storeCnf := func(serverName, clusterName, storeDir, conf string) string { 3346 switch { 3347 case strings.HasPrefix(serverName, "C1"): 3348 conf = strings.Replace(conf, "_MAXMEM_", fmt.Sprint(largeSystemLimit), 1) 3349 conf = strings.Replace(conf, "_MAXFILE_", fmt.Sprint(largeSystemLimit), 1) 3350 return strings.Replace(conf, "_TAG_", serverName, 1) 3351 case strings.HasPrefix(serverName, "C2"): 3352 conf = strings.Replace(conf, "_MAXMEM_", fmt.Sprint(smallSystemLimit), 1) 3353 conf = strings.Replace(conf, "_MAXFILE_", fmt.Sprint(smallSystemLimit), 1) 3354 return strings.Replace(conf, "_TAG_", serverName, 1) 3355 default: 3356 return conf 3357 } 3358 } 3359 3360 sCluster := createJetStreamSuperClusterWithTemplateAndModHook(t, tmpl, 3, 2, storeCnf, nil) 3361 defer sCluster.shutdown() 3362 3363 requestLeaderStepDown := func(clientURL string) error { 3364 nc, err := nats.Connect(clientURL) 3365 if err != nil { 3366 return err 3367 } 3368 defer nc.Close() 3369 3370 ncResp, err := nc.Request(JSApiLeaderStepDown, nil, 3*time.Second) 3371 if err != nil { 3372 return err 3373 } 3374 3375 var resp JSApiLeaderStepDownResponse 3376 if err := json.Unmarshal(ncResp.Data, &resp); err != nil { 3377 return err 3378 } 3379 if resp.Error != nil { 3380 return resp.Error 3381 } 3382 if !resp.Success { 3383 return fmt.Errorf("leader step down request not successful") 3384 } 3385 3386 return nil 3387 } 3388 3389 // Force large cluster to be leader 3390 var largeLeader *Server 3391 err := checkForErr(15*time.Second, 500*time.Millisecond, func() error { 3392 // Range over cluster A, which is the large cluster. 3393 servers := sCluster.clusters[0].servers 3394 for _, s := range servers { 3395 if s.JetStreamIsLeader() { 3396 largeLeader = s 3397 return nil 3398 } 3399 } 3400 3401 if err := requestLeaderStepDown(servers[0].ClientURL()); err != nil { 3402 return fmt.Errorf("failed to request leader step down: %s", err) 3403 } 3404 return fmt.Errorf("leader is not in large cluster") 3405 }) 3406 if err != nil { 3407 t.Skipf("failed to get desired layout: %s", err) 3408 } 3409 3410 getStreams := func(jsm nats.JetStreamManager) []string { 3411 var streams []string 3412 for s := range jsm.StreamNames() { 3413 streams = append(streams, s) 3414 } 3415 return streams 3416 } 3417 nc, js := jsClientConnect(t, largeLeader) 3418 defer nc.Close() 3419 3420 cases := []struct { 3421 name string 3422 storage nats.StorageType 3423 createMaxBytes int64 3424 serverTag string 3425 wantErr bool 3426 }{ 3427 { 3428 name: "file create large stream on small cluster b0", 3429 storage: nats.FileStorage, 3430 createMaxBytes: smallSystemLimit + 1, 3431 serverTag: "C2-S1", 3432 wantErr: true, 3433 }, 3434 { 3435 name: "memory create large stream on small cluster b0", 3436 storage: nats.MemoryStorage, 3437 createMaxBytes: smallSystemLimit + 1, 3438 serverTag: "C2-S1", 3439 wantErr: true, 3440 }, 3441 { 3442 name: "file create large stream on small cluster b1", 3443 storage: nats.FileStorage, 3444 createMaxBytes: smallSystemLimit + 1, 3445 serverTag: "C2-S2", 3446 wantErr: true, 3447 }, 3448 { 3449 name: "memory create large stream on small cluster b1", 3450 storage: nats.MemoryStorage, 3451 createMaxBytes: smallSystemLimit + 1, 3452 serverTag: "C2-S2", 3453 wantErr: true, 3454 }, 3455 { 3456 name: "file create large stream on small cluster b2", 3457 storage: nats.FileStorage, 3458 createMaxBytes: smallSystemLimit + 1, 3459 serverTag: "C2-S3", 3460 wantErr: true, 3461 }, 3462 { 3463 name: "memory create large stream on small cluster b2", 3464 storage: nats.MemoryStorage, 3465 createMaxBytes: smallSystemLimit + 1, 3466 serverTag: "C2-S3", 3467 wantErr: true, 3468 }, 3469 { 3470 name: "file create large stream on large cluster a0", 3471 storage: nats.FileStorage, 3472 createMaxBytes: smallSystemLimit + 1, 3473 serverTag: "C1-S1", 3474 }, 3475 { 3476 name: "memory create large stream on large cluster a0", 3477 storage: nats.MemoryStorage, 3478 createMaxBytes: smallSystemLimit + 1, 3479 serverTag: "C1-S1", 3480 }, 3481 { 3482 name: "file create large stream on large cluster a1", 3483 storage: nats.FileStorage, 3484 createMaxBytes: smallSystemLimit + 1, 3485 serverTag: "C1-S2", 3486 }, 3487 { 3488 name: "memory create large stream on large cluster a1", 3489 storage: nats.MemoryStorage, 3490 createMaxBytes: smallSystemLimit + 1, 3491 serverTag: "C1-S2", 3492 }, 3493 { 3494 name: "file create large stream on large cluster a2", 3495 storage: nats.FileStorage, 3496 createMaxBytes: smallSystemLimit + 1, 3497 serverTag: "C1-S3", 3498 }, 3499 { 3500 name: "memory create large stream on large cluster a2", 3501 storage: nats.MemoryStorage, 3502 createMaxBytes: smallSystemLimit + 1, 3503 serverTag: "C1-S3", 3504 }, 3505 } 3506 for i := 0; i < len(cases) && !t.Failed(); i++ { 3507 c := cases[i] 3508 t.Run(c.name, func(st *testing.T) { 3509 var clusterName string 3510 if strings.HasPrefix(c.serverTag, "a") { 3511 clusterName = "cluster-a" 3512 } else if strings.HasPrefix(c.serverTag, "b") { 3513 clusterName = "cluster-b" 3514 } 3515 3516 if s := getStreams(js); len(s) != 0 { 3517 st.Fatalf("unexpected stream count, got=%d, want=0", len(s)) 3518 } 3519 3520 streamName := fmt.Sprintf("TEST-%s", c.serverTag) 3521 si, err := js.AddStream(&nats.StreamConfig{ 3522 Name: streamName, 3523 Subjects: []string{"foo"}, 3524 Storage: c.storage, 3525 MaxBytes: c.createMaxBytes, 3526 Placement: &nats.Placement{ 3527 Cluster: clusterName, 3528 Tags: []string{c.serverTag}, 3529 }, 3530 }) 3531 if c.wantErr && err == nil { 3532 if s := getStreams(js); len(s) != 1 { 3533 st.Logf("unexpected stream count, got=%d, want=1, streams=%v", len(s), s) 3534 } 3535 3536 cfg := si.Config 3537 st.Fatalf("unexpected success, maxBytes=%d, cluster=%s, tags=%v", 3538 cfg.MaxBytes, cfg.Placement.Cluster, cfg.Placement.Tags) 3539 } else if !c.wantErr && err != nil { 3540 if s := getStreams(js); len(s) != 0 { 3541 st.Logf("unexpected stream count, got=%d, want=0, streams=%v", len(s), s) 3542 } 3543 3544 require_NoError(st, err) 3545 } 3546 3547 if err == nil { 3548 if s := getStreams(js); len(s) != 1 { 3549 st.Fatalf("unexpected stream count, got=%d, want=1", len(s)) 3550 } 3551 } 3552 // Delete regardless. 3553 js.DeleteStream(streamName) 3554 }) 3555 } 3556 } 3557 3558 func TestJetStreamSuperClusterMixedModeSwitchToInterestOnlyStaticConfig(t *testing.T) { 3559 tmpl := ` 3560 listen: 127.0.0.1:-1 3561 server_name: %s 3562 jetstream: { domain: ngs, max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 3563 leaf: { listen: 127.0.0.1:-1 } 3564 cluster { 3565 name: %s 3566 listen: 127.0.0.1:%d 3567 routes = [%s] 3568 } 3569 accounts { 3570 ONE { 3571 users = [ { user: "one", pass: "pwd" } ] 3572 jetstream: enabled 3573 } 3574 TWO { users = [ { user: "two", pass: "pwd" } ] } 3575 $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } 3576 } 3577 ` 3578 sc := createJetStreamSuperClusterWithTemplateAndModHook(t, tmpl, 5, 3, 3579 func(serverName, clusterName, storeDir, conf string) string { 3580 sname := serverName[strings.Index(serverName, "-")+1:] 3581 switch sname { 3582 case "S4", "S5": 3583 conf = strings.ReplaceAll(conf, "jetstream: { ", "#jetstream: { ") 3584 default: 3585 conf = strings.ReplaceAll(conf, "leaf: { ", "#leaf: { ") 3586 } 3587 return conf 3588 }, nil) 3589 defer sc.shutdown() 3590 3591 // Connect our client to a non JS server 3592 c := sc.randomCluster() 3593 var s *Server 3594 for _, as := range c.servers { 3595 if !as.JetStreamEnabled() { 3596 s = as 3597 break 3598 } 3599 } 3600 if s == nil { 3601 t.Fatal("Did not find a non JS server!") 3602 } 3603 nc, js := jsClientConnect(t, s, nats.UserInfo("one", "pwd")) 3604 defer nc.Close() 3605 3606 // Just create a stream and then make sure that all gateways have switched 3607 // to interest-only mode. 3608 si, err := js.AddStream(&nats.StreamConfig{Name: "interest", Replicas: 3}) 3609 require_NoError(t, err) 3610 3611 sc.waitOnStreamLeader("ONE", "interest") 3612 3613 check := func(accName string) { 3614 t.Helper() 3615 for _, c := range sc.clusters { 3616 for _, s := range c.servers { 3617 // Check only JS servers outbound GW connections 3618 if !s.JetStreamEnabled() { 3619 continue 3620 } 3621 opts := s.getOpts() 3622 for _, gw := range opts.Gateway.Gateways { 3623 if gw.Name == opts.Gateway.Name { 3624 continue 3625 } 3626 checkGWInterestOnlyMode(t, s, gw.Name, accName) 3627 } 3628 } 3629 } 3630 } 3631 // Starting v2.9.0, all accounts should be switched to interest-only mode 3632 check("ONE") 3633 check("TWO") 3634 3635 var gwsa [16]*client 3636 gws := gwsa[:0] 3637 3638 s = sc.serverByName(si.Cluster.Leader) 3639 // Get the GW outbound connections 3640 s.getOutboundGatewayConnections(&gws) 3641 for _, gwc := range gws { 3642 gwc.mu.Lock() 3643 gwc.nc.Close() 3644 gwc.mu.Unlock() 3645 } 3646 waitForOutboundGateways(t, s, 2, 5*time.Second) 3647 check("ONE") 3648 check("TWO") 3649 } 3650 3651 func TestJetStreamSuperClusterMixedModeSwitchToInterestOnlyOperatorConfig(t *testing.T) { 3652 kp, _ := nkeys.FromSeed(oSeed) 3653 3654 skp, _ := nkeys.CreateAccount() 3655 spub, _ := skp.PublicKey() 3656 nac := jwt.NewAccountClaims(spub) 3657 sjwt, err := nac.Encode(kp) 3658 require_NoError(t, err) 3659 3660 akp, _ := nkeys.CreateAccount() 3661 apub, _ := akp.PublicKey() 3662 nac = jwt.NewAccountClaims(apub) 3663 // Set some limits to enable JS. 3664 nac.Limits.JetStreamLimits.DiskStorage = 1024 * 1024 3665 nac.Limits.JetStreamLimits.Streams = 10 3666 ajwt, err := nac.Encode(kp) 3667 require_NoError(t, err) 3668 3669 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3670 if strings.HasSuffix(r.URL.Path, spub) { 3671 w.Write([]byte(sjwt)) 3672 } else { 3673 w.Write([]byte(ajwt)) 3674 } 3675 })) 3676 defer ts.Close() 3677 3678 operator := fmt.Sprintf(` 3679 operator: %s 3680 resolver: URL("%s/ngs/v1/accounts/jwt/") 3681 `, ojwt, ts.URL) 3682 3683 tmpl := ` 3684 listen: 127.0.0.1:-1 3685 server_name: %s 3686 jetstream: { domain: ngs, max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 3687 leaf: { listen: 127.0.0.1:-1 } 3688 cluster { 3689 name: %s 3690 listen: 127.0.0.1:%d 3691 routes = [%s] 3692 } 3693 ` + operator 3694 sc := createJetStreamSuperClusterWithTemplateAndModHook(t, tmpl, 5, 3, 3695 func(serverName, clusterName, storeDir, conf string) string { 3696 conf = strings.ReplaceAll(conf, "system_account: \"$SYS\"", fmt.Sprintf("system_account: \"%s\"", spub)) 3697 sname := serverName[strings.Index(serverName, "-")+1:] 3698 switch sname { 3699 case "S4", "S5": 3700 conf = strings.ReplaceAll(conf, "jetstream: { ", "#jetstream: { ") 3701 default: 3702 conf = strings.ReplaceAll(conf, "leaf: { ", "#leaf: { ") 3703 } 3704 return conf 3705 }, nil) 3706 defer sc.shutdown() 3707 3708 // Connect our client to a non JS server 3709 c := sc.randomCluster() 3710 var s *Server 3711 for _, as := range c.servers { 3712 if !as.JetStreamEnabled() { 3713 s = as 3714 break 3715 } 3716 } 3717 if s == nil { 3718 t.Fatal("Did not find a non JS server!") 3719 } 3720 nc, js := jsClientConnect(t, s, createUserCreds(t, nil, akp)) 3721 defer nc.Close() 3722 3723 // Just create a stream and then make sure that all gateways have switched 3724 // to interest-only mode. 3725 si, err := js.AddStream(&nats.StreamConfig{Name: "interest", Replicas: 3}) 3726 require_NoError(t, err) 3727 3728 sc.waitOnStreamLeader(apub, "interest") 3729 3730 check := func(s *Server) { 3731 opts := s.getOpts() 3732 for _, gw := range opts.Gateway.Gateways { 3733 if gw.Name == opts.Gateway.Name { 3734 continue 3735 } 3736 checkGWInterestOnlyMode(t, s, gw.Name, apub) 3737 } 3738 } 3739 s = sc.serverByName(si.Cluster.Leader) 3740 check(s) 3741 3742 // Let's cause a leadership change and verify that it still works. 3743 _, err = nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "interest"), nil, time.Second) 3744 require_NoError(t, err) 3745 sc.waitOnStreamLeader(apub, "interest") 3746 3747 si, err = js.StreamInfo("interest") 3748 require_NoError(t, err) 3749 s = sc.serverByName(si.Cluster.Leader) 3750 check(s) 3751 3752 var gwsa [16]*client 3753 gws := gwsa[:0] 3754 // Get the GW outbound connections 3755 s.getOutboundGatewayConnections(&gws) 3756 for _, gwc := range gws { 3757 gwc.mu.Lock() 3758 gwc.nc.Close() 3759 gwc.mu.Unlock() 3760 } 3761 waitForOutboundGateways(t, s, 2, 5*time.Second) 3762 check(s) 3763 } 3764 3765 type captureGWRewriteLogger struct { 3766 DummyLogger 3767 ch chan string 3768 } 3769 3770 func (l *captureGWRewriteLogger) Tracef(format string, args ...any) { 3771 msg := fmt.Sprintf(format, args...) 3772 if strings.Contains(msg, "$JS.SNAPSHOT.ACK.TEST") && strings.Contains(msg, gwReplyPrefix) { 3773 select { 3774 case l.ch <- msg: 3775 default: 3776 } 3777 } 3778 } 3779 3780 func TestJetStreamSuperClusterGWReplyRewrite(t *testing.T) { 3781 sc := createJetStreamSuperCluster(t, 3, 2) 3782 defer sc.shutdown() 3783 3784 nc, js := jsClientConnect(t, sc.serverByName("C1-S1")) 3785 defer nc.Close() 3786 3787 _, err := js.AddStream(&nats.StreamConfig{ 3788 Name: "TEST", 3789 Subjects: []string{"foo"}, 3790 Replicas: 3, 3791 }) 3792 require_NoError(t, err) 3793 sc.waitOnStreamLeader(globalAccountName, "TEST") 3794 3795 for i := 0; i < 10; i++ { 3796 sendStreamMsg(t, nc, "foo", "msg") 3797 } 3798 3799 nc2, _ := jsClientConnect(t, sc.serverByName("C2-S2")) 3800 defer nc2.Close() 3801 3802 s := sc.clusters[0].streamLeader(globalAccountName, "TEST") 3803 var gws []*client 3804 s.getOutboundGatewayConnections(&gws) 3805 for _, gw := range gws { 3806 gw.mu.Lock() 3807 gw.trace = true 3808 gw.mu.Unlock() 3809 } 3810 l := &captureGWRewriteLogger{ch: make(chan string, 1)} 3811 s.SetLogger(l, false, true) 3812 3813 // Send a request through the gateway 3814 sreq := &JSApiStreamSnapshotRequest{ 3815 DeliverSubject: nats.NewInbox(), 3816 ChunkSize: 512, 3817 } 3818 natsSub(t, nc2, sreq.DeliverSubject, func(m *nats.Msg) { 3819 m.Respond(nil) 3820 }) 3821 natsFlush(t, nc2) 3822 req, _ := json.Marshal(sreq) 3823 rmsg, err := nc2.Request(fmt.Sprintf(JSApiStreamSnapshotT, "TEST"), req, time.Second) 3824 require_NoError(t, err) 3825 var resp JSApiStreamSnapshotResponse 3826 err = json.Unmarshal(rmsg.Data, &resp) 3827 require_NoError(t, err) 3828 if resp.Error != nil { 3829 t.Fatalf("Did not get correct error response: %+v", resp.Error) 3830 } 3831 3832 // Now we just want to make sure that the reply has the gateway prefix 3833 select { 3834 case <-l.ch: 3835 case <-time.After(10 * time.Second): 3836 } 3837 } 3838 3839 func TestJetStreamSuperClusterGWOfflineSatus(t *testing.T) { 3840 orgEventsHBInterval := eventsHBInterval 3841 eventsHBInterval = 500 * time.Millisecond //time.Second 3842 defer func() { eventsHBInterval = orgEventsHBInterval }() 3843 3844 tmpl := ` 3845 listen: 127.0.0.1:-1 3846 server_name: %s 3847 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 3848 3849 gateway { 3850 name: "local" 3851 listen: 127.0.0.1:-1 3852 } 3853 3854 cluster { 3855 name: %s 3856 listen: 127.0.0.1:%d 3857 routes = [%s] 3858 } 3859 3860 accounts { 3861 SYS { 3862 users [{user: sys, password: pwd}] 3863 } 3864 ONE { 3865 jetstream: enabled 3866 users [{user: one, password: pwd}] 3867 } 3868 } 3869 system_account=SYS 3870 ` 3871 c := createJetStreamClusterWithTemplate(t, tmpl, "local", 3) 3872 defer c.shutdown() 3873 3874 var gwURLs string 3875 for i, s := range c.servers { 3876 if i > 0 { 3877 gwURLs += "," 3878 } 3879 gwURLs += `"nats://` + s.GatewayAddr().String() + `"` 3880 } 3881 3882 tmpl2 := ` 3883 listen: 127.0.0.1:-1 3884 server_name: %s 3885 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 3886 3887 gateway { 3888 name: "remote" 3889 listen: 127.0.0.1:-1 3890 __remote__ 3891 } 3892 3893 cluster { 3894 name: %s 3895 listen: 127.0.0.1:%d 3896 routes = [%s] 3897 } 3898 3899 accounts { 3900 SYS { 3901 users [{user: sys, password: pwd}] 3902 } 3903 ONE { 3904 jetstream: enabled 3905 users [{user: one, password: pwd}] 3906 } 3907 } 3908 system_account=SYS 3909 ` 3910 c2 := createJetStreamClusterAndModHook(t, tmpl2, "remote", "R", 2, 16022, false, 3911 func(serverName, clusterName, storeDir, conf string) string { 3912 conf = strings.Replace(conf, "__remote__", fmt.Sprintf("gateways [ { name: 'local', urls: [%s] } ]", gwURLs), 1) 3913 return conf 3914 }) 3915 defer c2.shutdown() 3916 3917 for _, s := range c.servers { 3918 waitForOutboundGateways(t, s, 1, 2*time.Second) 3919 } 3920 for _, s := range c2.servers { 3921 waitForOutboundGateways(t, s, 1, 2*time.Second) 3922 } 3923 c.waitOnPeerCount(5) 3924 3925 // Simulate going offline without sending shutdown protocol 3926 for _, s := range c2.servers { 3927 c := s.getOutboundGatewayConnection("local") 3928 c.setNoReconnect() 3929 c.mu.Lock() 3930 c.nc.Close() 3931 c.mu.Unlock() 3932 } 3933 c2.shutdown() 3934 3935 checkFor(t, 10*time.Second, 100*time.Millisecond, func() error { 3936 var ok int 3937 for _, s := range c.servers { 3938 jsz, err := s.Jsz(nil) 3939 if err != nil { 3940 return err 3941 } 3942 for _, r := range jsz.Meta.Replicas { 3943 if r.Name == "RS-1" && r.Offline { 3944 ok++ 3945 } else if r.Name == "RS-2" && r.Offline { 3946 ok++ 3947 } 3948 } 3949 } 3950 if ok != 2 { 3951 return fmt.Errorf("RS-1 or RS-2 still marked as online") 3952 } 3953 return nil 3954 }) 3955 } 3956 3957 func TestJetStreamSuperClusterMovingR1Stream(t *testing.T) { 3958 // Make C2 have some latency. 3959 gwm := gwProxyMap{ 3960 "C2": &gwProxy{ 3961 rtt: 10 * time.Millisecond, 3962 up: 1 * 1024 * 1024 * 1024, // 1gbit 3963 down: 1 * 1024 * 1024 * 1024, // 1gbit 3964 }, 3965 } 3966 sc := createJetStreamTaggedSuperClusterWithGWProxy(t, gwm) 3967 defer sc.shutdown() 3968 3969 nc, js := jsClientConnect(t, sc.clusterForName("C1").randomServer()) 3970 defer nc.Close() 3971 3972 _, err := js.AddStream(&nats.StreamConfig{ 3973 Name: "TEST", 3974 }) 3975 require_NoError(t, err) 3976 3977 toSend := 10_000 3978 for i := 0; i < toSend; i++ { 3979 _, err := js.PublishAsync("TEST", []byte("HELLO WORLD")) 3980 require_NoError(t, err) 3981 } 3982 select { 3983 case <-js.PublishAsyncComplete(): 3984 case <-time.After(5 * time.Second): 3985 t.Fatalf("Did not receive completion signal") 3986 } 3987 3988 // Have it move to GCP. 3989 _, err = js.UpdateStream(&nats.StreamConfig{ 3990 Name: "TEST", 3991 Placement: &nats.Placement{Tags: []string{"cloud:gcp"}}, 3992 }) 3993 require_NoError(t, err) 3994 3995 checkFor(t, 5*time.Second, 100*time.Millisecond, func() error { 3996 sc.waitOnStreamLeader(globalAccountName, "TEST") 3997 si, err := js.StreamInfo("TEST") 3998 if err != nil { 3999 return err 4000 } 4001 if si.Cluster.Name != "C2" { 4002 return fmt.Errorf("Wrong cluster: %q", si.Cluster.Name) 4003 } 4004 if si.Cluster.Leader == _EMPTY_ { 4005 return fmt.Errorf("No leader yet") 4006 } else if !strings.HasPrefix(si.Cluster.Leader, "C2") { 4007 return fmt.Errorf("Wrong leader: %q", si.Cluster.Leader) 4008 } 4009 // Now we want to see that we shrink back to original. 4010 if len(si.Cluster.Replicas) != 0 { 4011 return fmt.Errorf("Expected 0 replicas, got %d", len(si.Cluster.Replicas)) 4012 } 4013 if si.State.Msgs != uint64(toSend) { 4014 return fmt.Errorf("Only see %d msgs", si.State.Msgs) 4015 } 4016 return nil 4017 }) 4018 } 4019 4020 // https://github.com/nats-io/nats-server/issues/4396 4021 func TestJetStreamSuperClusterR1StreamPeerRemove(t *testing.T) { 4022 sc := createJetStreamSuperCluster(t, 1, 3) 4023 defer sc.shutdown() 4024 4025 nc, js := jsClientConnect(t, sc.serverByName("C1-S1")) 4026 defer nc.Close() 4027 4028 _, err := js.AddStream(&nats.StreamConfig{ 4029 Name: "TEST", 4030 Subjects: []string{"foo"}, 4031 Replicas: 1, 4032 }) 4033 require_NoError(t, err) 4034 4035 si, err := js.StreamInfo("TEST") 4036 require_NoError(t, err) 4037 4038 // Call peer remove on the only peer the leader. 4039 resp, err := nc.Request(fmt.Sprintf(JSApiStreamRemovePeerT, "TEST"), []byte(`{"peer":"`+si.Cluster.Leader+`"}`), time.Second) 4040 require_NoError(t, err) 4041 var rpr JSApiStreamRemovePeerResponse 4042 require_NoError(t, json.Unmarshal(resp.Data, &rpr)) 4043 require_False(t, rpr.Success) 4044 require_True(t, rpr.Error.ErrCode == 10075) 4045 4046 // Stream should still be in place and useable. 4047 _, err = js.StreamInfo("TEST") 4048 require_NoError(t, err) 4049 } 4050 4051 func TestJetStreamSuperClusterConsumerPauseAdvisories(t *testing.T) { 4052 sc := createJetStreamSuperCluster(t, 3, 3) 4053 defer sc.shutdown() 4054 4055 nc, js := jsClientConnect(t, sc.randomServer()) 4056 defer nc.Close() 4057 4058 pauseReq := func(consumer string, deadline time.Time) time.Time { 4059 j, err := json.Marshal(JSApiConsumerPauseRequest{ 4060 PauseUntil: deadline, 4061 }) 4062 require_NoError(t, err) 4063 msg, err := nc.Request(fmt.Sprintf(JSApiConsumerPauseT, "TEST", consumer), j, time.Second) 4064 require_NoError(t, err) 4065 var res JSApiConsumerPauseResponse 4066 err = json.Unmarshal(msg.Data, &res) 4067 require_NoError(t, err) 4068 return res.PauseUntil 4069 } 4070 4071 checkAdvisory := func(msg *nats.Msg, shouldBePaused bool, deadline time.Time) { 4072 t.Helper() 4073 var advisory JSConsumerPauseAdvisory 4074 require_NoError(t, json.Unmarshal(msg.Data, &advisory)) 4075 require_Equal(t, advisory.Stream, "TEST") 4076 require_Equal(t, advisory.Consumer, "my_consumer") 4077 require_Equal(t, advisory.Paused, shouldBePaused) 4078 require_True(t, advisory.PauseUntil.Equal(deadline)) 4079 } 4080 4081 _, err := js.AddStream(&nats.StreamConfig{ 4082 Name: "TEST", 4083 Subjects: []string{"foo"}, 4084 Replicas: 3, 4085 }) 4086 require_NoError(t, err) 4087 4088 ch := make(chan *nats.Msg, 10) 4089 _, err = nc.ChanSubscribe(JSAdvisoryConsumerPausePre+".TEST.my_consumer", ch) 4090 require_NoError(t, err) 4091 4092 deadline := time.Now().Add(time.Second) 4093 jsTestPause_CreateOrUpdateConsumer(t, nc, ActionCreate, "TEST", ConsumerConfig{ 4094 Name: "my_consumer", 4095 PauseUntil: &deadline, 4096 Replicas: 3, 4097 }) 4098 4099 // First advisory should tell us that the consumer was paused 4100 // on creation. 4101 msg := require_ChanRead(t, ch, time.Second*2) 4102 checkAdvisory(msg, true, deadline) 4103 require_Len(t, len(ch), 0) // Should only receive one advisory. 4104 4105 // The second one for the unpause. 4106 msg = require_ChanRead(t, ch, time.Second*2) 4107 checkAdvisory(msg, false, deadline) 4108 require_Len(t, len(ch), 0) // Should only receive one advisory. 4109 4110 // Now we'll pause the consumer for a second using the API. 4111 deadline = time.Now().Add(time.Second) 4112 require_True(t, pauseReq("my_consumer", deadline).Equal(deadline)) 4113 4114 // Third advisory should tell us about the pause via the API. 4115 msg = require_ChanRead(t, ch, time.Second*2) 4116 checkAdvisory(msg, true, deadline) 4117 require_Len(t, len(ch), 0) // Should only receive one advisory. 4118 4119 // Finally that should unpause. 4120 msg = require_ChanRead(t, ch, time.Second*2) 4121 checkAdvisory(msg, false, deadline) 4122 require_Len(t, len(ch), 0) // Should only receive one advisory. 4123 4124 // Now we're going to set the deadline into the future so we can 4125 // see what happens when we kick leaders or restart. 4126 deadline = time.Now().Add(time.Hour) 4127 require_True(t, pauseReq("my_consumer", deadline).Equal(deadline)) 4128 4129 // Setting the deadline should have generated an advisory. 4130 msg = require_ChanRead(t, ch, time.Second*2) 4131 checkAdvisory(msg, true, deadline) 4132 require_Len(t, len(ch), 0) // Should only receive one advisory. 4133 }