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

     1  // Copyright 2020-2023 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Do not exlude this file with the !skip_js_tests since those helpers
    15  // are also used by MQTT.
    16  
    17  package server
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"math/rand"
    24  	"net"
    25  	"net/url"
    26  	"os"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/nats-io/nats.go"
    33  	"golang.org/x/time/rate"
    34  )
    35  
    36  // Support functions
    37  
    38  func init() {
    39  	// Speed up raft for tests.
    40  	hbInterval = 50 * time.Millisecond
    41  	minElectionTimeout = 750 * time.Millisecond
    42  	maxElectionTimeout = 2500 * time.Millisecond
    43  	lostQuorumInterval = 500 * time.Millisecond
    44  	lostQuorumCheck = 4 * hbInterval
    45  }
    46  
    47  // Used to setup clusters of clusters for tests.
    48  type cluster struct {
    49  	servers  []*Server
    50  	opts     []*Options
    51  	name     string
    52  	t        testing.TB
    53  	nproxies []*netProxy
    54  }
    55  
    56  // Used to setup superclusters for tests.
    57  type supercluster struct {
    58  	t        *testing.T
    59  	clusters []*cluster
    60  	nproxies []*netProxy
    61  }
    62  
    63  func (sc *supercluster) shutdown() {
    64  	if sc == nil {
    65  		return
    66  	}
    67  	for _, np := range sc.nproxies {
    68  		np.stop()
    69  	}
    70  	for _, c := range sc.clusters {
    71  		shutdownCluster(c)
    72  	}
    73  }
    74  
    75  func (sc *supercluster) randomServer() *Server {
    76  	return sc.randomCluster().randomServer()
    77  }
    78  
    79  func (sc *supercluster) serverByName(sname string) *Server {
    80  	for _, c := range sc.clusters {
    81  		if s := c.serverByName(sname); s != nil {
    82  			return s
    83  		}
    84  	}
    85  	return nil
    86  }
    87  
    88  func (sc *supercluster) waitOnStreamLeader(account, stream string) {
    89  	sc.t.Helper()
    90  	expires := time.Now().Add(30 * time.Second)
    91  	for time.Now().Before(expires) {
    92  		for _, c := range sc.clusters {
    93  			if leader := c.streamLeader(account, stream); leader != nil {
    94  				time.Sleep(200 * time.Millisecond)
    95  				return
    96  			}
    97  		}
    98  		time.Sleep(100 * time.Millisecond)
    99  	}
   100  	sc.t.Fatalf("Expected a stream leader for %q %q, got none", account, stream)
   101  }
   102  
   103  var jsClusterAccountsTempl = `
   104  	listen: 127.0.0.1:-1
   105  
   106  	server_name: %s
   107  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   108  
   109  	leaf {
   110  		listen: 127.0.0.1:-1
   111  	}
   112  
   113  	cluster {
   114  		name: %s
   115  		listen: 127.0.0.1:%d
   116  		routes = [%s]
   117  	}
   118  
   119  	websocket {
   120  		listen: 127.0.0.1:-1
   121  		compression: true
   122  		handshake_timeout: "5s"
   123  		no_tls: true
   124  	}
   125  
   126  	no_auth_user: one
   127  
   128  	accounts {
   129  		ONE { users = [ { user: "one", pass: "p" } ]; jetstream: enabled }
   130  		TWO { users = [ { user: "two", pass: "p" } ]; jetstream: enabled }
   131  		NOJS { users = [ { user: "nojs", pass: "p" } ] }
   132  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   133  	}
   134  `
   135  
   136  var jsClusterTempl = `
   137  	listen: 127.0.0.1:-1
   138  	server_name: %s
   139  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   140  
   141  	leaf {
   142  		listen: 127.0.0.1:-1
   143  	}
   144  
   145  	cluster {
   146  		name: %s
   147  		listen: 127.0.0.1:%d
   148  		routes = [%s]
   149  	}
   150  
   151  	# For access to system account.
   152  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   153  `
   154  
   155  var jsClusterEncryptedTempl = `
   156  	listen: 127.0.0.1:-1
   157  	server_name: %s
   158  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s', key: "s3cr3t!"}
   159  
   160  	leaf {
   161  		listen: 127.0.0.1:-1
   162  	}
   163  
   164  	cluster {
   165  		name: %s
   166  		listen: 127.0.0.1:%d
   167  		routes = [%s]
   168  	}
   169  
   170  	# For access to system account.
   171  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   172  `
   173  
   174  var jsClusterMaxBytesTempl = `
   175  	listen: 127.0.0.1:-1
   176  	server_name: %s
   177  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   178  
   179  	leaf {
   180  		listen: 127.0.0.1:-1
   181  	}
   182  
   183  	cluster {
   184  		name: %s
   185  		listen: 127.0.0.1:%d
   186  		routes = [%s]
   187  	}
   188  
   189  	no_auth_user: u
   190  
   191  	accounts {
   192  		$U {
   193  			users = [ { user: "u", pass: "p" } ]
   194  			jetstream: {
   195  				max_mem:   128MB
   196  				max_file:  18GB
   197  				max_bytes: true // Forces streams to indicate max_bytes.
   198  			}
   199  		}
   200  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   201  	}
   202  `
   203  
   204  var jsClusterMaxBytesAccountLimitTempl = `
   205  	listen: 127.0.0.1:-1
   206  	server_name: %s
   207  	jetstream: {max_mem_store: 256MB, max_file_store: 4GB, store_dir: '%s'}
   208  
   209  	leaf {
   210  		listen: 127.0.0.1:-1
   211  	}
   212  
   213  	cluster {
   214  		name: %s
   215  		listen: 127.0.0.1:%d
   216  		routes = [%s]
   217  	}
   218  
   219  	no_auth_user: u
   220  
   221  	accounts {
   222  		$U {
   223  			users = [ { user: "u", pass: "p" } ]
   224  			jetstream: {
   225  				max_mem:   128MB
   226  				max_file:  3GB
   227  				max_bytes: true // Forces streams to indicate max_bytes.
   228  			}
   229  		}
   230  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   231  	}
   232  `
   233  
   234  var jsSuperClusterTempl = `
   235  	%s
   236  	gateway {
   237  		name: %s
   238  		listen: 127.0.0.1:%d
   239  		gateways = [%s
   240  		]
   241  	}
   242  
   243  	system_account: "$SYS"
   244  `
   245  
   246  var jsClusterLimitsTempl = `
   247  	listen: 127.0.0.1:-1
   248  	server_name: %s
   249  	jetstream: {max_mem_store: 2MB, max_file_store: 8MB, store_dir: '%s'}
   250  
   251  	cluster {
   252  		name: %s
   253  		listen: 127.0.0.1:%d
   254  		routes = [%s]
   255  	}
   256  
   257  	no_auth_user: u
   258  
   259  	accounts {
   260  		ONE {
   261  			users = [ { user: "u", pass: "s3cr3t!" } ]
   262  			jetstream: enabled
   263  		}
   264  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   265  	}
   266  `
   267  
   268  var jsMixedModeGlobalAccountTempl = `
   269  	listen: 127.0.0.1:-1
   270  	server_name: %s
   271  	jetstream: {max_mem_store: 2MB, max_file_store: 8MB, store_dir: '%s'}
   272  
   273  	cluster {
   274  		name: %s
   275  		listen: 127.0.0.1:%d
   276  		routes = [%s]
   277  	}
   278  
   279  	accounts {$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   280  `
   281  
   282  var jsGWTempl = `%s{name: %s, urls: [%s]}`
   283  
   284  var jsClusterAccountLimitsTempl = `
   285  	listen: 127.0.0.1:-1
   286  	server_name: %s
   287  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   288  
   289  	cluster {
   290  		name: %s
   291  		listen: 127.0.0.1:%d
   292  		routes = [%s]
   293  	}
   294  
   295  	no_auth_user: js
   296  
   297  	accounts {
   298  		$JS { users = [ { user: "js", pass: "p" } ]; jetstream: {max_store: 1MB, max_mem: 0} }
   299  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   300  	}
   301  `
   302  
   303  func createJetStreamTaggedSuperCluster(t *testing.T) *supercluster {
   304  	return createJetStreamTaggedSuperClusterWithGWProxy(t, nil)
   305  }
   306  
   307  func createJetStreamTaggedSuperClusterWithGWProxy(t *testing.T, gwm gwProxyMap) *supercluster {
   308  	sc := createJetStreamSuperClusterWithTemplateAndModHook(t, jsClusterTempl, 3, 3, nil, gwm)
   309  	sc.waitOnPeerCount(9)
   310  
   311  	reset := func(s *Server) {
   312  		s.mu.Lock()
   313  		rch := s.sys.resetCh
   314  		s.mu.Unlock()
   315  		if rch != nil {
   316  			rch <- struct{}{}
   317  		}
   318  		s.sendStatszUpdate()
   319  	}
   320  
   321  	// Make first cluster AWS, US country code.
   322  	for _, s := range sc.clusterForName("C1").servers {
   323  		s.optsMu.Lock()
   324  		s.opts.Tags.Add("cloud:aws")
   325  		s.opts.Tags.Add("country:us")
   326  		s.optsMu.Unlock()
   327  		reset(s)
   328  	}
   329  	// Make second cluster GCP, UK country code.
   330  	for _, s := range sc.clusterForName("C2").servers {
   331  		s.optsMu.Lock()
   332  		s.opts.Tags.Add("cloud:gcp")
   333  		s.opts.Tags.Add("country:uk")
   334  		s.optsMu.Unlock()
   335  		reset(s)
   336  	}
   337  	// Make third cluster AZ, JP country code.
   338  	for _, s := range sc.clusterForName("C3").servers {
   339  		s.optsMu.Lock()
   340  		s.opts.Tags.Add("cloud:az")
   341  		s.opts.Tags.Add("country:jp")
   342  		s.optsMu.Unlock()
   343  		reset(s)
   344  	}
   345  
   346  	ml := sc.leader()
   347  	js := ml.getJetStream()
   348  	require_True(t, js != nil)
   349  	js.mu.RLock()
   350  	defer js.mu.RUnlock()
   351  	cc := js.cluster
   352  	require_True(t, cc != nil)
   353  
   354  	// Walk and make sure all tags are registered.
   355  	expires := time.Now().Add(10 * time.Second)
   356  	for time.Now().Before(expires) {
   357  		allOK := true
   358  		for _, p := range cc.meta.Peers() {
   359  			si, ok := ml.nodeToInfo.Load(p.ID)
   360  			require_True(t, ok)
   361  			ni := si.(nodeInfo)
   362  			if len(ni.tags) == 0 {
   363  				allOK = false
   364  				reset(sc.serverByName(ni.name))
   365  			}
   366  		}
   367  		if allOK {
   368  			break
   369  		}
   370  	}
   371  
   372  	return sc
   373  }
   374  
   375  func createJetStreamSuperCluster(t *testing.T, numServersPer, numClusters int) *supercluster {
   376  	return createJetStreamSuperClusterWithTemplate(t, jsClusterTempl, numServersPer, numClusters)
   377  }
   378  
   379  func createJetStreamSuperClusterWithTemplate(t *testing.T, tmpl string, numServersPer, numClusters int) *supercluster {
   380  	return createJetStreamSuperClusterWithTemplateAndModHook(t, tmpl, numServersPer, numClusters, nil, nil)
   381  }
   382  
   383  // For doing proxyies in GWs.
   384  type gwProxy struct {
   385  	rtt  time.Duration
   386  	up   int
   387  	down int
   388  }
   389  
   390  // For use in normal clusters.
   391  type clusterProxy = gwProxy
   392  
   393  // Maps cluster names to proxy settings.
   394  type gwProxyMap map[string]*gwProxy
   395  
   396  func createJetStreamSuperClusterWithTemplateAndModHook(t *testing.T, tmpl string, numServersPer, numClusters int, modify modifyCb, gwm gwProxyMap) *supercluster {
   397  	t.Helper()
   398  	if numServersPer < 1 {
   399  		t.Fatalf("Number of servers must be >= 1")
   400  	}
   401  	if numClusters <= 1 {
   402  		t.Fatalf("Number of clusters must be > 1")
   403  	}
   404  
   405  	startClusterPorts := []int{20_022, 22_022, 24_022}
   406  	startGatewayPorts := []int{20_122, 22_122, 24_122}
   407  	startClusterPort := startClusterPorts[rand.Intn(len(startClusterPorts))]
   408  	startGWPort := startGatewayPorts[rand.Intn(len(startGatewayPorts))]
   409  
   410  	// Make the GWs form faster for the tests.
   411  	SetGatewaysSolicitDelay(10 * time.Millisecond)
   412  	defer ResetGatewaysSolicitDelay()
   413  
   414  	cp, gp := startClusterPort, startGWPort
   415  	var clusters []*cluster
   416  	var nproxies []*netProxy
   417  	var gws []string
   418  
   419  	// Build GWs first, will be same for all servers.
   420  	for i, port := 1, gp; i <= numClusters; i++ {
   421  		cn := fmt.Sprintf("C%d", i)
   422  		var gwp *gwProxy
   423  		if len(gwm) > 0 {
   424  			gwp = gwm[cn]
   425  		}
   426  		var urls []string
   427  		for n := 0; n < numServersPer; n++ {
   428  			routeURL := fmt.Sprintf("nats-route://127.0.0.1:%d", port)
   429  			if gwp != nil {
   430  				np := createNetProxy(gwp.rtt, gwp.up, gwp.down, routeURL, false)
   431  				nproxies = append(nproxies, np)
   432  				routeURL = np.routeURL()
   433  			}
   434  			urls = append(urls, routeURL)
   435  			port++
   436  		}
   437  		gws = append(gws, fmt.Sprintf(jsGWTempl, "\n\t\t\t", cn, strings.Join(urls, ",")))
   438  	}
   439  	gwconf := strings.Join(gws, _EMPTY_)
   440  
   441  	for i := 1; i <= numClusters; i++ {
   442  		cn := fmt.Sprintf("C%d", i)
   443  		// Go ahead and build configurations.
   444  		c := &cluster{servers: make([]*Server, 0, numServersPer), opts: make([]*Options, 0, numServersPer), name: cn}
   445  
   446  		// Build out the routes that will be shared with all configs.
   447  		var routes []string
   448  		for port := cp; port < cp+numServersPer; port++ {
   449  			routes = append(routes, fmt.Sprintf("nats-route://127.0.0.1:%d", port))
   450  		}
   451  		routeConfig := strings.Join(routes, ",")
   452  
   453  		for si := 0; si < numServersPer; si++ {
   454  			storeDir := t.TempDir()
   455  			sn := fmt.Sprintf("%s-S%d", cn, si+1)
   456  			bconf := fmt.Sprintf(tmpl, sn, storeDir, cn, cp+si, routeConfig)
   457  			conf := fmt.Sprintf(jsSuperClusterTempl, bconf, cn, gp, gwconf)
   458  			gp++
   459  			if modify != nil {
   460  				conf = modify(sn, cn, storeDir, conf)
   461  			}
   462  			s, o := RunServerWithConfig(createConfFile(t, []byte(conf)))
   463  			c.servers = append(c.servers, s)
   464  			c.opts = append(c.opts, o)
   465  		}
   466  		checkClusterFormed(t, c.servers...)
   467  		clusters = append(clusters, c)
   468  		cp += numServersPer
   469  		c.t = t
   470  	}
   471  
   472  	// Start any proxies.
   473  	for _, np := range nproxies {
   474  		np.start()
   475  	}
   476  
   477  	// Wait for the supercluster to be formed.
   478  	egws := numClusters - 1
   479  	for _, c := range clusters {
   480  		for _, s := range c.servers {
   481  			waitForOutboundGateways(t, s, egws, 10*time.Second)
   482  		}
   483  	}
   484  
   485  	sc := &supercluster{t, clusters, nproxies}
   486  	sc.waitOnLeader()
   487  	sc.waitOnAllCurrent()
   488  
   489  	// Wait for all the peer nodes to be registered.
   490  	checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
   491  		var peers []string
   492  		if ml := sc.leader(); ml != nil {
   493  			peers = ml.ActivePeers()
   494  			if len(peers) == numClusters*numServersPer {
   495  				return nil
   496  			}
   497  		}
   498  		return fmt.Errorf("Not correct number of peers, expected %d, got %d", numClusters*numServersPer, len(peers))
   499  	})
   500  
   501  	if sc.leader() == nil {
   502  		sc.t.Fatalf("Expected a cluster leader, got none")
   503  	}
   504  
   505  	return sc
   506  }
   507  
   508  func (sc *supercluster) createLeafNodes(clusterName string, numServers int) *cluster {
   509  	sc.t.Helper()
   510  
   511  	// Create our leafnode cluster template first.
   512  	return sc.createLeafNodesWithDomain(clusterName, numServers, "")
   513  }
   514  
   515  func (sc *supercluster) createLeafNodesWithDomain(clusterName string, numServers int, domain string) *cluster {
   516  	sc.t.Helper()
   517  
   518  	// Create our leafnode cluster template first.
   519  	return sc.randomCluster().createLeafNodes(clusterName, numServers, domain)
   520  }
   521  
   522  func (sc *supercluster) createSingleLeafNode(extend bool) *Server {
   523  	sc.t.Helper()
   524  
   525  	return sc.randomCluster().createLeafNode(extend)
   526  }
   527  
   528  func (sc *supercluster) leader() *Server {
   529  	for _, c := range sc.clusters {
   530  		if leader := c.leader(); leader != nil {
   531  			return leader
   532  		}
   533  	}
   534  	return nil
   535  }
   536  
   537  func (sc *supercluster) waitOnLeader() {
   538  	sc.t.Helper()
   539  	expires := time.Now().Add(30 * time.Second)
   540  	for time.Now().Before(expires) {
   541  		for _, c := range sc.clusters {
   542  			if leader := c.leader(); leader != nil {
   543  				time.Sleep(250 * time.Millisecond)
   544  				return
   545  			}
   546  		}
   547  		time.Sleep(25 * time.Millisecond)
   548  	}
   549  	sc.t.Fatalf("Expected a cluster leader, got none")
   550  }
   551  
   552  func (sc *supercluster) waitOnAllCurrent() {
   553  	sc.t.Helper()
   554  	for _, c := range sc.clusters {
   555  		c.waitOnAllCurrent()
   556  	}
   557  }
   558  
   559  func (sc *supercluster) clusterForName(name string) *cluster {
   560  	for _, c := range sc.clusters {
   561  		if c.name == name {
   562  			return c
   563  		}
   564  	}
   565  	return nil
   566  }
   567  
   568  func (sc *supercluster) randomCluster() *cluster {
   569  	clusters := append(sc.clusters[:0:0], sc.clusters...)
   570  	rand.Shuffle(len(clusters), func(i, j int) { clusters[i], clusters[j] = clusters[j], clusters[i] })
   571  	return clusters[0]
   572  }
   573  
   574  func (sc *supercluster) waitOnPeerCount(n int) {
   575  	sc.t.Helper()
   576  	sc.waitOnLeader()
   577  	leader := sc.leader()
   578  	expires := time.Now().Add(30 * time.Second)
   579  	for time.Now().Before(expires) {
   580  		peers := leader.JetStreamClusterPeers()
   581  		if len(peers) == n {
   582  			return
   583  		}
   584  		time.Sleep(100 * time.Millisecond)
   585  	}
   586  	sc.t.Fatalf("Expected a super cluster peer count of %d, got %d", n, len(leader.JetStreamClusterPeers()))
   587  }
   588  
   589  var jsClusterMirrorSourceImportsTempl = `
   590  	listen: 127.0.0.1:-1
   591  	server_name: %s
   592  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   593  
   594  	cluster {
   595  		name: %s
   596  		listen: 127.0.0.1:%d
   597  		routes = [%s]
   598  	}
   599  
   600  	no_auth_user: dlc
   601  
   602  	accounts {
   603  		JS {
   604  			jetstream: enabled
   605  			users = [ { user: "rip", pass: "pass" } ]
   606  			exports [
   607  				{ service: "$JS.API.CONSUMER.>" } # To create internal consumers to mirror/source.
   608  				{ stream: "RI.DELIVER.SYNC.>" }   # For the mirror/source consumers sending to IA via delivery subject.
   609  				{ service: "$JS.FC.>" }
   610  			]
   611  		}
   612  		IA {
   613  			jetstream: enabled
   614  			users = [ { user: "dlc", pass: "pass" } ]
   615  			imports [
   616  				{ service: { account: JS, subject: "$JS.API.CONSUMER.>"}, to: "RI.JS.API.CONSUMER.>" }
   617  				{ stream: { account: JS, subject: "RI.DELIVER.SYNC.>"} }
   618  				{ service: {account: JS, subject: "$JS.FC.>" }}
   619  			]
   620  		}
   621  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   622  	}
   623  `
   624  
   625  var jsClusterImportsTempl = `
   626  	listen: 127.0.0.1:-1
   627  	server_name: %s
   628  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   629  
   630  	cluster {
   631  		name: %s
   632  		listen: 127.0.0.1:%d
   633  		routes = [%s]
   634  	}
   635  
   636  	no_auth_user: dlc
   637  
   638  	accounts {
   639  		JS {
   640  			jetstream: enabled
   641  			users = [ { user: "rip", pass: "pass" } ]
   642  			exports [
   643  				{ service: "$JS.API.>", response: stream }
   644  				{ service: "TEST" } # For publishing to the stream.
   645  				{ service: "$JS.ACK.TEST.*.>" }
   646  			]
   647  		}
   648  		IA {
   649  			users = [ { user: "dlc", pass: "pass" } ]
   650  			imports [
   651  				{ service: { subject: "$JS.API.>", account: JS }}
   652  				{ service: { subject: "TEST", account: JS }}
   653  				{ service: { subject: "$JS.ACK.TEST.*.>", account: JS }}
   654  			]
   655  		}
   656  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   657  	}
   658  `
   659  
   660  func createMixedModeCluster(t testing.TB, tmpl string, clusterName, snPre string, numJsServers, numNonServers int, doJSConfig bool) *cluster {
   661  	t.Helper()
   662  
   663  	if clusterName == _EMPTY_ || numJsServers < 0 || numNonServers < 1 {
   664  		t.Fatalf("Bad params")
   665  	}
   666  
   667  	numServers := numJsServers + numNonServers
   668  	const startClusterPort = 23232
   669  
   670  	// Build out the routes that will be shared with all configs.
   671  	var routes []string
   672  	for cp := startClusterPort; cp < startClusterPort+numServers; cp++ {
   673  		routes = append(routes, fmt.Sprintf("nats-route://127.0.0.1:%d", cp))
   674  	}
   675  	routeConfig := strings.Join(routes, ",")
   676  
   677  	// Go ahead and build configurations and start servers.
   678  	c := &cluster{servers: make([]*Server, 0, numServers), opts: make([]*Options, 0, numServers), name: clusterName}
   679  
   680  	for cp := startClusterPort; cp < startClusterPort+numServers; cp++ {
   681  		storeDir := t.TempDir()
   682  
   683  		sn := fmt.Sprintf("%sS-%d", snPre, cp-startClusterPort+1)
   684  		conf := fmt.Sprintf(tmpl, sn, storeDir, clusterName, cp, routeConfig)
   685  
   686  		// Disable JS here.
   687  		if cp-startClusterPort >= numJsServers {
   688  			// We can disable by commmenting it out, meaning no JS config, or can set the config up and just set disabled.
   689  			// e.g. jetstream: {domain: "SPOKE", enabled: false}
   690  			if doJSConfig {
   691  				conf = strings.Replace(conf, "jetstream: {", "jetstream: { enabled: false, ", 1)
   692  			} else {
   693  				conf = strings.Replace(conf, "jetstream: ", "# jetstream: ", 1)
   694  			}
   695  		}
   696  
   697  		s, o := RunServerWithConfig(createConfFile(t, []byte(conf)))
   698  		c.servers = append(c.servers, s)
   699  		c.opts = append(c.opts, o)
   700  	}
   701  	c.t = t
   702  
   703  	// Wait til we are formed and have a leader.
   704  	c.checkClusterFormed()
   705  	if numJsServers > 0 {
   706  		c.waitOnPeerCount(numJsServers)
   707  	}
   708  
   709  	return c
   710  }
   711  
   712  // This will create a cluster that is explicitly configured for the routes, etc.
   713  // and also has a defined clustername. All configs for routes and cluster name will be the same.
   714  func createJetStreamClusterExplicit(t testing.TB, clusterName string, numServers int) *cluster {
   715  	return createJetStreamClusterWithTemplate(t, jsClusterTempl, clusterName, numServers)
   716  }
   717  
   718  func createJetStreamClusterWithTemplate(t testing.TB, tmpl string, clusterName string, numServers int) *cluster {
   719  	return createJetStreamClusterWithTemplateAndModHook(t, tmpl, clusterName, numServers, nil)
   720  }
   721  
   722  func createJetStreamClusterWithTemplateAndModHook(t testing.TB, tmpl string, clusterName string, numServers int, modify modifyCb) *cluster {
   723  	startPorts := []int{7_022, 9_022, 11_022, 15_022}
   724  	port := startPorts[rand.Intn(len(startPorts))]
   725  	return createJetStreamClusterAndModHook(t, tmpl, clusterName, _EMPTY_, numServers, port, true, modify)
   726  }
   727  
   728  func createJetStreamCluster(t testing.TB, tmpl string, clusterName, snPre string, numServers int, portStart int, waitOnReady bool) *cluster {
   729  	return createJetStreamClusterAndModHook(t, tmpl, clusterName, snPre, numServers, portStart, waitOnReady, nil)
   730  }
   731  
   732  type modifyCb func(serverName, clusterName, storeDir, conf string) string
   733  
   734  func createJetStreamClusterAndModHook(t testing.TB, tmpl, cName, snPre string, numServers int, portStart int, waitOnReady bool, modify modifyCb) *cluster {
   735  	return createJetStreamClusterEx(t, tmpl, cName, snPre, numServers, portStart, waitOnReady, modify, nil)
   736  }
   737  
   738  func createJetStreamClusterWithNetProxy(t testing.TB, cName string, numServers int, cnp *clusterProxy) *cluster {
   739  	startPorts := []int{7_122, 9_122, 11_122, 15_122}
   740  	port := startPorts[rand.Intn(len(startPorts))]
   741  	return createJetStreamClusterEx(t, jsClusterTempl, cName, _EMPTY_, numServers, port, true, nil, cnp)
   742  }
   743  
   744  func createJetStreamClusterEx(t testing.TB, tmpl, cName, snPre string, numServers int, portStart int, wait bool, modify modifyCb, cnp *clusterProxy) *cluster {
   745  	t.Helper()
   746  	if cName == _EMPTY_ || numServers < 1 {
   747  		t.Fatalf("Bad params")
   748  	}
   749  
   750  	// Flaky test prevention:
   751  	// Binding a socket to IP stack port 0 will bind an ephemeral port from an OS-specific range.
   752  	// If someone passes in to us a port spec which would cover that range, the test would be flaky.
   753  	// Adjust these ports to be the most inclusive across the port runner OSes.
   754  	// Linux: /proc/sys/net/ipv4/ip_local_port_range : 32768:60999
   755  	// <https://dataplane.org/ephemeralports.html> is useful, and shows there's no safe available range without OS-specific tuning.
   756  	// Our tests are usually run on Linux.  Folks who care about other OSes: if you can't tune your test-runner OS to match, please
   757  	// propose a viable alternative.
   758  	const prohibitedPortFirst = 32768
   759  	const prohibitedPortLast = 60999
   760  	if (portStart >= prohibitedPortFirst && portStart <= prohibitedPortLast) ||
   761  		(portStart+numServers-1 >= prohibitedPortFirst && portStart+numServers-1 <= prohibitedPortLast) {
   762  		t.Fatalf("test setup failure: may not specify a cluster port range which falls within %d:%d", prohibitedPortFirst, prohibitedPortLast)
   763  	}
   764  
   765  	// Build out the routes that will be shared with all configs.
   766  	var routes []string
   767  	var nproxies []*netProxy
   768  	for cp := portStart; cp < portStart+numServers; cp++ {
   769  		routeURL := fmt.Sprintf("nats-route://127.0.0.1:%d", cp)
   770  		if cnp != nil {
   771  			np := createNetProxy(cnp.rtt, cnp.up, cnp.down, routeURL, false)
   772  			nproxies = append(nproxies, np)
   773  			routeURL = np.routeURL()
   774  		}
   775  		routes = append(routes, routeURL)
   776  	}
   777  	routeConfig := strings.Join(routes, ",")
   778  
   779  	// Go ahead and build configurations and start servers.
   780  	c := &cluster{servers: make([]*Server, 0, numServers), opts: make([]*Options, 0, numServers), name: cName, nproxies: nproxies}
   781  
   782  	// Start any proxies.
   783  	for _, np := range nproxies {
   784  		np.start()
   785  	}
   786  
   787  	for cp := portStart; cp < portStart+numServers; cp++ {
   788  		storeDir := t.TempDir()
   789  		sn := fmt.Sprintf("%sS-%d", snPre, cp-portStart+1)
   790  		conf := fmt.Sprintf(tmpl, sn, storeDir, cName, cp, routeConfig)
   791  		if modify != nil {
   792  			conf = modify(sn, cName, storeDir, conf)
   793  		}
   794  		s, o := RunServerWithConfig(createConfFile(t, []byte(conf)))
   795  		c.servers = append(c.servers, s)
   796  		c.opts = append(c.opts, o)
   797  	}
   798  	c.t = t
   799  
   800  	// Wait til we are formed and have a leader.
   801  	c.checkClusterFormed()
   802  	if wait {
   803  		c.waitOnClusterReady()
   804  	}
   805  
   806  	return c
   807  }
   808  
   809  func (c *cluster) addInNewServer() *Server {
   810  	c.t.Helper()
   811  	sn := fmt.Sprintf("S-%d", len(c.servers)+1)
   812  	storeDir := c.t.TempDir()
   813  	seedRoute := fmt.Sprintf("nats-route://127.0.0.1:%d", c.opts[0].Cluster.Port)
   814  	conf := fmt.Sprintf(jsClusterTempl, sn, storeDir, c.name, -1, seedRoute)
   815  	s, o := RunServerWithConfig(createConfFile(c.t, []byte(conf)))
   816  	c.servers = append(c.servers, s)
   817  	c.opts = append(c.opts, o)
   818  	c.checkClusterFormed()
   819  	return s
   820  }
   821  
   822  // This is tied to jsClusterAccountsTempl, so changes there to users needs to be reflected here.
   823  func (c *cluster) createSingleLeafNodeNoSystemAccount() *Server {
   824  	as := c.randomServer()
   825  	lno := as.getOpts().LeafNode
   826  	ln1 := fmt.Sprintf("nats://one:p@%s:%d", lno.Host, lno.Port)
   827  	ln2 := fmt.Sprintf("nats://two:p@%s:%d", lno.Host, lno.Port)
   828  	conf := fmt.Sprintf(jsClusterSingleLeafNodeTempl, c.t.TempDir(), ln1, ln2)
   829  	s, o := RunServerWithConfig(createConfFile(c.t, []byte(conf)))
   830  	c.servers = append(c.servers, s)
   831  	c.opts = append(c.opts, o)
   832  
   833  	checkLeafNodeConnectedCount(c.t, as, 2)
   834  
   835  	return s
   836  }
   837  
   838  // This is tied to jsClusterAccountsTempl, so changes there to users needs to be reflected here.
   839  func (c *cluster) createSingleLeafNodeNoSystemAccountAndEnablesJetStream() *Server {
   840  	return c.createSingleLeafNodeNoSystemAccountAndEnablesJetStreamWithDomain(_EMPTY_, "nojs")
   841  }
   842  
   843  func (c *cluster) createSingleLeafNodeNoSystemAccountAndEnablesJetStreamWithDomain(domain, user string) *Server {
   844  	tmpl := jsClusterSingleLeafNodeLikeNGSTempl
   845  	if domain != _EMPTY_ {
   846  		nsc := fmt.Sprintf("domain: %s, store_dir:", domain)
   847  		tmpl = strings.Replace(jsClusterSingleLeafNodeLikeNGSTempl, "store_dir:", nsc, 1)
   848  	}
   849  	as := c.randomServer()
   850  	lno := as.getOpts().LeafNode
   851  	ln := fmt.Sprintf("nats://%s:p@%s:%d", user, lno.Host, lno.Port)
   852  	conf := fmt.Sprintf(tmpl, c.t.TempDir(), ln)
   853  	s, o := RunServerWithConfig(createConfFile(c.t, []byte(conf)))
   854  	c.servers = append(c.servers, s)
   855  	c.opts = append(c.opts, o)
   856  
   857  	checkLeafNodeConnectedCount(c.t, as, 1)
   858  
   859  	return s
   860  }
   861  
   862  var jsClusterSingleLeafNodeLikeNGSTempl = `
   863  	listen: 127.0.0.1:-1
   864  	server_name: LNJS
   865  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   866  
   867  	leaf { remotes [ { urls: [ %s ] } ] }
   868  `
   869  
   870  var jsClusterSingleLeafNodeTempl = `
   871  	listen: 127.0.0.1:-1
   872  	server_name: LNJS
   873  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   874  
   875  	leaf { remotes [
   876  		{ urls: [ %s ], account: "JSY" }
   877  		{ urls: [ %s ], account: "JSN" } ]
   878  	}
   879  
   880  	accounts {
   881  		JSY { users = [ { user: "y", pass: "p" } ]; jetstream: true }
   882  		JSN { users = [ { user: "n", pass: "p" } ] }
   883  		$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
   884  	}
   885  `
   886  
   887  var jsClusterTemplWithLeafNode = `
   888  	listen: 127.0.0.1:-1
   889  	server_name: %s
   890  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   891  
   892  	{{leaf}}
   893  
   894  	cluster {
   895  		name: %s
   896  		listen: 127.0.0.1:%d
   897  		routes = [%s]
   898  	}
   899  
   900  	# For access to system account.
   901  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   902  `
   903  
   904  var jsClusterTemplWithLeafNodeNoJS = `
   905  	listen: 127.0.0.1:-1
   906  	server_name: %s
   907  
   908  	# Need to keep below since it fills in the store dir by default so just comment out.
   909  	# jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   910  
   911  	{{leaf}}
   912  
   913  	cluster {
   914  		name: %s
   915  		listen: 127.0.0.1:%d
   916  		routes = [%s]
   917  	}
   918  
   919  	# For access to system account.
   920  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   921  `
   922  
   923  var jsClusterTemplWithSingleLeafNode = `
   924  	listen: 127.0.0.1:-1
   925  	server_name: %s
   926  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   927  
   928  	{{leaf}}
   929  
   930  	# For access to system account.
   931  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   932  `
   933  
   934  var jsClusterTemplWithSingleFleetLeafNode = `
   935  	listen: 127.0.0.1:-1
   936  	server_name: %s
   937  	cluster: { name: fleet }
   938  	jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
   939  
   940  	{{leaf}}
   941  
   942  	# For access to system account.
   943  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   944  `
   945  
   946  var jsClusterTemplWithSingleLeafNodeNoJS = `
   947  	listen: 127.0.0.1:-1
   948  	server_name: %s
   949  
   950  	# jetstream: {store_dir: '%s'}
   951  
   952  	{{leaf}}
   953  
   954  	# For access to system account.
   955  	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
   956  `
   957  
   958  var jsLeafFrag = `
   959  	leaf {
   960  		remotes [
   961  			{ urls: [ %s ] }
   962  			{ urls: [ %s ], account: "$SYS" }
   963  		]
   964  	}
   965  `
   966  var jsLeafNoSysFrag = `
   967  	leaf {
   968  		remotes [
   969  			{ urls: [ %s ] }
   970  		]
   971  	}
   972  `
   973  
   974  func (c *cluster) createLeafNodes(clusterName string, numServers int, domain string) *cluster {
   975  	c.t.Helper()
   976  	return c.createLeafNodesWithStartPortAndDomain(clusterName, numServers, 22111, domain)
   977  }
   978  
   979  func (c *cluster) createLeafNodesNoJS(clusterName string, numServers int) *cluster {
   980  	c.t.Helper()
   981  	return c.createLeafNodesWithTemplateAndStartPort(jsClusterTemplWithLeafNodeNoJS, clusterName, numServers, 21333)
   982  }
   983  
   984  func (c *cluster) createLeafNodesWithStartPortAndDomain(clusterName string, numServers int, portStart int, domain string) *cluster {
   985  	c.t.Helper()
   986  	if domain == _EMPTY_ {
   987  		return c.createLeafNodesWithTemplateAndStartPort(jsClusterTemplWithLeafNode, clusterName, numServers, portStart)
   988  	}
   989  	tmpl := strings.Replace(jsClusterTemplWithLeafNode, "store_dir:", fmt.Sprintf(`domain: "%s", store_dir:`, domain), 1)
   990  	return c.createLeafNodesWithTemplateAndStartPort(tmpl, clusterName, numServers, portStart)
   991  }
   992  
   993  func (c *cluster) createLeafNode(extend bool) *Server {
   994  	c.t.Helper()
   995  	if extend {
   996  		return c.createLeafNodeWithTemplate("LNS",
   997  			strings.ReplaceAll(jsClusterTemplWithSingleLeafNode, "store_dir:", " extension_hint: will_extend, store_dir:"))
   998  	} else {
   999  		return c.createLeafNodeWithTemplate("LNS", jsClusterTemplWithSingleLeafNode)
  1000  	}
  1001  }
  1002  
  1003  func (c *cluster) createLeafNodeWithTemplate(name, template string) *Server {
  1004  	c.t.Helper()
  1005  	tmpl := c.createLeafSolicit(template)
  1006  	conf := fmt.Sprintf(tmpl, name, c.t.TempDir())
  1007  	s, o := RunServerWithConfig(createConfFile(c.t, []byte(conf)))
  1008  	c.servers = append(c.servers, s)
  1009  	c.opts = append(c.opts, o)
  1010  	return s
  1011  }
  1012  
  1013  func (c *cluster) createLeafNodeWithTemplateNoSystem(name, template string) *Server {
  1014  	return c.createLeafNodeWithTemplateNoSystemWithProto(name, template, "nats")
  1015  }
  1016  
  1017  func (c *cluster) createLeafNodeWithTemplateNoSystemWithProto(name, template, proto string) *Server {
  1018  	c.t.Helper()
  1019  	tmpl := c.createLeafSolicitNoSystemWithProto(template, proto)
  1020  	conf := fmt.Sprintf(tmpl, name, c.t.TempDir())
  1021  	s, o := RunServerWithConfig(createConfFile(c.t, []byte(conf)))
  1022  	c.servers = append(c.servers, s)
  1023  	c.opts = append(c.opts, o)
  1024  	return s
  1025  }
  1026  
  1027  // Helper to generate the leaf solicit configs.
  1028  func (c *cluster) createLeafSolicit(tmpl string) string {
  1029  	return c.createLeafSolicitWithProto(tmpl, "nats")
  1030  }
  1031  
  1032  func (c *cluster) createLeafSolicitWithProto(tmpl, proto string) string {
  1033  	c.t.Helper()
  1034  
  1035  	// Create our leafnode cluster template first.
  1036  	var lns, lnss []string
  1037  	for _, s := range c.servers {
  1038  		if s.ClusterName() != c.name {
  1039  			continue
  1040  		}
  1041  		ln := s.getOpts().LeafNode
  1042  		lns = append(lns, fmt.Sprintf("%s://%s:%d", proto, ln.Host, ln.Port))
  1043  		lnss = append(lnss, fmt.Sprintf("%s://admin:s3cr3t!@%s:%d", proto, ln.Host, ln.Port))
  1044  	}
  1045  	lnc := strings.Join(lns, ", ")
  1046  	lnsc := strings.Join(lnss, ", ")
  1047  	lconf := fmt.Sprintf(jsLeafFrag, lnc, lnsc)
  1048  	return strings.Replace(tmpl, "{{leaf}}", lconf, 1)
  1049  }
  1050  
  1051  func (c *cluster) createLeafSolicitNoSystemWithProto(tmpl, proto string) string {
  1052  	c.t.Helper()
  1053  
  1054  	// Create our leafnode cluster template first.
  1055  	var lns []string
  1056  	for _, s := range c.servers {
  1057  		if s.ClusterName() != c.name {
  1058  			continue
  1059  		}
  1060  		switch proto {
  1061  		case "nats", "tls":
  1062  			ln := s.getOpts().LeafNode
  1063  			lns = append(lns, fmt.Sprintf("%s://%s:%d", proto, ln.Host, ln.Port))
  1064  		case "ws", "wss":
  1065  			ln := s.getOpts().Websocket
  1066  			lns = append(lns, fmt.Sprintf("%s://%s:%d", proto, ln.Host, ln.Port))
  1067  		}
  1068  	}
  1069  	lnc := strings.Join(lns, ", ")
  1070  	return strings.Replace(tmpl, "{{leaf}}", fmt.Sprintf(jsLeafNoSysFrag, lnc), 1)
  1071  }
  1072  
  1073  func (c *cluster) createLeafNodesWithTemplateMixedMode(template, clusterName string, numJsServers, numNonServers int, doJSConfig bool) *cluster {
  1074  	c.t.Helper()
  1075  
  1076  	// Create our leafnode cluster template first.
  1077  	tmpl := c.createLeafSolicit(template)
  1078  	pre := clusterName + "-"
  1079  
  1080  	lc := createMixedModeCluster(c.t, tmpl, clusterName, pre, numJsServers, numNonServers, doJSConfig)
  1081  	for _, s := range lc.servers {
  1082  		checkLeafNodeConnectedCount(c.t, s, 2)
  1083  	}
  1084  	lc.waitOnClusterReadyWithNumPeers(numJsServers)
  1085  
  1086  	return lc
  1087  }
  1088  
  1089  func (c *cluster) createLeafNodesWithTemplateAndStartPort(template, clusterName string, numServers int, portStart int) *cluster {
  1090  	c.t.Helper()
  1091  
  1092  	// Create our leafnode cluster template first.
  1093  	tmpl := c.createLeafSolicit(template)
  1094  	pre := clusterName + "-"
  1095  	lc := createJetStreamCluster(c.t, tmpl, clusterName, pre, numServers, portStart, false)
  1096  	for _, s := range lc.servers {
  1097  		checkLeafNodeConnectedCount(c.t, s, 2)
  1098  	}
  1099  	return lc
  1100  }
  1101  
  1102  // Helper function to close and disable leafnodes.
  1103  func (s *Server) closeAndDisableLeafnodes() {
  1104  	var leafs []*client
  1105  	s.mu.Lock()
  1106  	for _, ln := range s.leafs {
  1107  		leafs = append(leafs, ln)
  1108  	}
  1109  	// Disable leafnodes for now.
  1110  	s.leafDisableConnect = true
  1111  	s.mu.Unlock()
  1112  
  1113  	for _, ln := range leafs {
  1114  		ln.closeConnection(Revocation)
  1115  	}
  1116  }
  1117  
  1118  // Helper function to re-enable leafnode connections.
  1119  func (s *Server) reEnableLeafnodes() {
  1120  	s.mu.Lock()
  1121  	// Re-enable leafnodes.
  1122  	s.leafDisableConnect = false
  1123  	s.mu.Unlock()
  1124  }
  1125  
  1126  // Helper to set the remote migrate feature.
  1127  func (s *Server) setJetStreamMigrateOnRemoteLeaf() {
  1128  	s.mu.Lock()
  1129  	for _, cfg := range s.leafRemoteCfgs {
  1130  		cfg.JetStreamClusterMigrate = true
  1131  	}
  1132  	s.mu.Unlock()
  1133  }
  1134  
  1135  // Will add in the mapping for the account to each server.
  1136  func (c *cluster) addSubjectMapping(account, src, dest string) {
  1137  	c.t.Helper()
  1138  
  1139  	for _, s := range c.servers {
  1140  		if s.ClusterName() != c.name {
  1141  			continue
  1142  		}
  1143  		acc, err := s.LookupAccount(account)
  1144  		if err != nil {
  1145  			c.t.Fatalf("Unexpected error on %v: %v", s, err)
  1146  		}
  1147  		if err := acc.AddMapping(src, dest); err != nil {
  1148  			c.t.Fatalf("Error adding mapping: %v", err)
  1149  		}
  1150  	}
  1151  	// Make sure interest propagates.
  1152  	time.Sleep(200 * time.Millisecond)
  1153  }
  1154  
  1155  // Adjust limits for the given account.
  1156  func (c *cluster) updateLimits(account string, newLimits map[string]JetStreamAccountLimits) {
  1157  	c.t.Helper()
  1158  	for _, s := range c.servers {
  1159  		acc, err := s.LookupAccount(account)
  1160  		if err != nil {
  1161  			c.t.Fatalf("Unexpected error: %v", err)
  1162  		}
  1163  		if err := acc.UpdateJetStreamLimits(newLimits); err != nil {
  1164  			c.t.Fatalf("Unexpected error: %v", err)
  1165  		}
  1166  	}
  1167  }
  1168  
  1169  // Hack for staticcheck
  1170  var skip = func(t *testing.T) {
  1171  	t.SkipNow()
  1172  }
  1173  
  1174  func jsClientConnect(t testing.TB, s *Server, opts ...nats.Option) (*nats.Conn, nats.JetStreamContext) {
  1175  	t.Helper()
  1176  	nc, err := nats.Connect(s.ClientURL(), opts...)
  1177  	if err != nil {
  1178  		t.Fatalf("Failed to create client: %v", err)
  1179  	}
  1180  	js, err := nc.JetStream(nats.MaxWait(10 * time.Second))
  1181  	if err != nil {
  1182  		t.Fatalf("Unexpected error getting JetStream context: %v", err)
  1183  	}
  1184  	return nc, js
  1185  }
  1186  
  1187  func jsClientConnectEx(t testing.TB, s *Server, domain string, opts ...nats.Option) (*nats.Conn, nats.JetStreamContext) {
  1188  	t.Helper()
  1189  	nc, err := nats.Connect(s.ClientURL(), opts...)
  1190  	if err != nil {
  1191  		t.Fatalf("Failed to create client: %v", err)
  1192  	}
  1193  	js, err := nc.JetStream(nats.MaxWait(10*time.Second), nats.Domain(domain))
  1194  	if err != nil {
  1195  		t.Fatalf("Unexpected error getting JetStream context: %v", err)
  1196  	}
  1197  	return nc, js
  1198  }
  1199  
  1200  func jsClientConnectURL(t testing.TB, url string, opts ...nats.Option) (*nats.Conn, nats.JetStreamContext) {
  1201  	t.Helper()
  1202  
  1203  	nc, err := nats.Connect(url, opts...)
  1204  	if err != nil {
  1205  		t.Fatalf("Failed to create client: %v", err)
  1206  	}
  1207  	js, err := nc.JetStream(nats.MaxWait(10 * time.Second))
  1208  	if err != nil {
  1209  		t.Fatalf("Unexpected error getting JetStream context: %v", err)
  1210  	}
  1211  	return nc, js
  1212  }
  1213  
  1214  func checkSubsPending(t *testing.T, sub *nats.Subscription, numExpected int) {
  1215  	t.Helper()
  1216  	checkFor(t, 10*time.Second, 20*time.Millisecond, func() error {
  1217  		if nmsgs, _, err := sub.Pending(); err != nil || nmsgs != numExpected {
  1218  			return fmt.Errorf("Did not receive correct number of messages: %d vs %d", nmsgs, numExpected)
  1219  		}
  1220  		return nil
  1221  	})
  1222  }
  1223  
  1224  func fetchMsgs(t *testing.T, sub *nats.Subscription, numExpected int, totalWait time.Duration) []*nats.Msg {
  1225  	t.Helper()
  1226  	result := make([]*nats.Msg, 0, numExpected)
  1227  	for start, count, wait := time.Now(), numExpected, totalWait; len(result) != numExpected; {
  1228  		msgs, err := sub.Fetch(count, nats.MaxWait(wait))
  1229  		if err != nil {
  1230  			t.Fatal(err)
  1231  		}
  1232  		result = append(result, msgs...)
  1233  		count -= len(msgs)
  1234  		if wait = totalWait - time.Since(start); wait < 0 {
  1235  			break
  1236  		}
  1237  	}
  1238  	if len(result) != numExpected {
  1239  		t.Fatalf("Unexpected msg count, got %d, want %d", len(result), numExpected)
  1240  	}
  1241  	return result
  1242  }
  1243  
  1244  func (c *cluster) restartServer(rs *Server) *Server {
  1245  	c.t.Helper()
  1246  	index := -1
  1247  	var opts *Options
  1248  	for i, s := range c.servers {
  1249  		if s == rs {
  1250  			index = i
  1251  			break
  1252  		}
  1253  	}
  1254  	if index < 0 {
  1255  		c.t.Fatalf("Could not find server %v to restart", rs)
  1256  	}
  1257  	opts = c.opts[index]
  1258  	s, o := RunServerWithConfig(opts.ConfigFile)
  1259  	c.servers[index] = s
  1260  	c.opts[index] = o
  1261  	return s
  1262  }
  1263  
  1264  func (c *cluster) checkClusterFormed() {
  1265  	c.t.Helper()
  1266  	checkClusterFormed(c.t, c.servers...)
  1267  }
  1268  
  1269  func (c *cluster) waitOnPeerCount(n int) {
  1270  	c.t.Helper()
  1271  	c.waitOnLeader()
  1272  	leader := c.leader()
  1273  	for leader == nil {
  1274  		c.waitOnLeader()
  1275  		leader = c.leader()
  1276  	}
  1277  	expires := time.Now().Add(30 * time.Second)
  1278  	for time.Now().Before(expires) {
  1279  		if peers := leader.JetStreamClusterPeers(); len(peers) == n {
  1280  			return
  1281  		}
  1282  		time.Sleep(100 * time.Millisecond)
  1283  		leader = c.leader()
  1284  		for leader == nil {
  1285  			c.waitOnLeader()
  1286  			leader = c.leader()
  1287  		}
  1288  	}
  1289  	c.t.Fatalf("Expected a cluster peer count of %d, got %d", n, len(leader.JetStreamClusterPeers()))
  1290  }
  1291  
  1292  func (c *cluster) waitOnConsumerLeader(account, stream, consumer string) {
  1293  	c.t.Helper()
  1294  	expires := time.Now().Add(30 * time.Second)
  1295  	for time.Now().Before(expires) {
  1296  		if leader := c.consumerLeader(account, stream, consumer); leader != nil {
  1297  			time.Sleep(200 * time.Millisecond)
  1298  			return
  1299  		}
  1300  		time.Sleep(100 * time.Millisecond)
  1301  	}
  1302  	c.t.Fatalf("Expected a consumer leader for %q %q %q, got none", account, stream, consumer)
  1303  }
  1304  
  1305  func (c *cluster) consumerLeader(account, stream, consumer string) *Server {
  1306  	c.t.Helper()
  1307  	for _, s := range c.servers {
  1308  		if s.JetStreamIsConsumerLeader(account, stream, consumer) {
  1309  			return s
  1310  		}
  1311  	}
  1312  	return nil
  1313  }
  1314  
  1315  func (c *cluster) randomNonConsumerLeader(account, stream, consumer string) *Server {
  1316  	c.t.Helper()
  1317  	for _, s := range c.servers {
  1318  		if !s.JetStreamIsConsumerLeader(account, stream, consumer) {
  1319  			return s
  1320  		}
  1321  	}
  1322  	return nil
  1323  }
  1324  
  1325  func (c *cluster) waitOnStreamLeader(account, stream string) {
  1326  	c.t.Helper()
  1327  	expires := time.Now().Add(30 * time.Second)
  1328  	for time.Now().Before(expires) {
  1329  		if leader := c.streamLeader(account, stream); leader != nil {
  1330  			time.Sleep(200 * time.Millisecond)
  1331  			return
  1332  		}
  1333  		time.Sleep(100 * time.Millisecond)
  1334  	}
  1335  	c.t.Fatalf("Expected a stream leader for %q %q, got none", account, stream)
  1336  }
  1337  
  1338  func (c *cluster) randomNonStreamLeader(account, stream string) *Server {
  1339  	c.t.Helper()
  1340  	for _, s := range c.servers {
  1341  		if s.JetStreamIsStreamAssigned(account, stream) && !s.JetStreamIsStreamLeader(account, stream) {
  1342  			return s
  1343  		}
  1344  	}
  1345  	return nil
  1346  }
  1347  
  1348  func (c *cluster) streamLeader(account, stream string) *Server {
  1349  	c.t.Helper()
  1350  	for _, s := range c.servers {
  1351  		if s.JetStreamIsStreamLeader(account, stream) {
  1352  			return s
  1353  		}
  1354  	}
  1355  	return nil
  1356  }
  1357  
  1358  func (c *cluster) waitOnStreamCurrent(s *Server, account, stream string) {
  1359  	c.t.Helper()
  1360  	expires := time.Now().Add(30 * time.Second)
  1361  	for time.Now().Before(expires) {
  1362  		if s.JetStreamIsStreamCurrent(account, stream) {
  1363  			time.Sleep(100 * time.Millisecond)
  1364  			return
  1365  		}
  1366  		time.Sleep(100 * time.Millisecond)
  1367  	}
  1368  	c.t.Fatalf("Expected server %q to eventually be current for stream %q", s, stream)
  1369  }
  1370  
  1371  func (c *cluster) waitOnServerHealthz(s *Server) {
  1372  	c.t.Helper()
  1373  	expires := time.Now().Add(30 * time.Second)
  1374  	for time.Now().Before(expires) {
  1375  		hs := s.healthz(nil)
  1376  		if hs.Status == "ok" && hs.Error == _EMPTY_ {
  1377  			return
  1378  		}
  1379  		time.Sleep(100 * time.Millisecond)
  1380  	}
  1381  	c.t.Fatalf("Expected server %q to eventually return healthz 'ok', but got %q", s, s.healthz(nil).Error)
  1382  }
  1383  
  1384  func (c *cluster) waitOnServerCurrent(s *Server) {
  1385  	c.t.Helper()
  1386  	expires := time.Now().Add(30 * time.Second)
  1387  	for time.Now().Before(expires) {
  1388  		time.Sleep(100 * time.Millisecond)
  1389  		if !s.JetStreamEnabled() || s.JetStreamIsCurrent() {
  1390  			return
  1391  		}
  1392  	}
  1393  	c.t.Fatalf("Expected server %q to eventually be current", s)
  1394  }
  1395  
  1396  func (c *cluster) waitOnAllCurrent() {
  1397  	c.t.Helper()
  1398  	for _, cs := range c.servers {
  1399  		c.waitOnServerCurrent(cs)
  1400  	}
  1401  }
  1402  
  1403  func (c *cluster) serverByName(sname string) *Server {
  1404  	for _, s := range c.servers {
  1405  		if s.Name() == sname {
  1406  			return s
  1407  		}
  1408  	}
  1409  	return nil
  1410  }
  1411  
  1412  func (c *cluster) randomNonLeader() *Server {
  1413  	// range should randomize.. but..
  1414  	for _, s := range c.servers {
  1415  		if s.Running() && !s.JetStreamIsLeader() {
  1416  			return s
  1417  		}
  1418  	}
  1419  	return nil
  1420  }
  1421  
  1422  func (c *cluster) leader() *Server {
  1423  	for _, s := range c.servers {
  1424  		if s.JetStreamIsLeader() {
  1425  			return s
  1426  		}
  1427  	}
  1428  	return nil
  1429  }
  1430  
  1431  func (c *cluster) expectNoLeader() {
  1432  	c.t.Helper()
  1433  	expires := time.Now().Add(maxElectionTimeout)
  1434  	for time.Now().Before(expires) {
  1435  		if c.leader() == nil {
  1436  			return
  1437  		}
  1438  		time.Sleep(20 * time.Millisecond)
  1439  	}
  1440  	c.t.Fatalf("Expected no leader but have one")
  1441  }
  1442  
  1443  func (c *cluster) waitOnLeader() {
  1444  	c.t.Helper()
  1445  	expires := time.Now().Add(40 * time.Second)
  1446  	for time.Now().Before(expires) {
  1447  		if leader := c.leader(); leader != nil {
  1448  			time.Sleep(100 * time.Millisecond)
  1449  			return
  1450  		}
  1451  		time.Sleep(10 * time.Millisecond)
  1452  	}
  1453  
  1454  	c.t.Fatalf("Expected a cluster leader, got none")
  1455  }
  1456  
  1457  func (c *cluster) waitOnAccount(account string) {
  1458  	c.t.Helper()
  1459  	expires := time.Now().Add(40 * time.Second)
  1460  	for time.Now().Before(expires) {
  1461  		found := true
  1462  		for _, s := range c.servers {
  1463  			acc, err := s.fetchAccount(account)
  1464  			found = found && err == nil && acc != nil
  1465  		}
  1466  		if found {
  1467  			return
  1468  		}
  1469  		time.Sleep(100 * time.Millisecond)
  1470  		continue
  1471  	}
  1472  
  1473  	c.t.Fatalf("Expected account %q to exist but didn't", account)
  1474  }
  1475  
  1476  // Helper function to check that a cluster is formed
  1477  func (c *cluster) waitOnClusterReady() {
  1478  	c.t.Helper()
  1479  	c.waitOnClusterReadyWithNumPeers(len(c.servers))
  1480  }
  1481  
  1482  func (c *cluster) waitOnClusterReadyWithNumPeers(numPeersExpected int) {
  1483  	c.t.Helper()
  1484  	var leader *Server
  1485  	expires := time.Now().Add(40 * time.Second)
  1486  	for time.Now().Before(expires) {
  1487  		if leader = c.leader(); leader != nil {
  1488  			break
  1489  		}
  1490  		time.Sleep(50 * time.Millisecond)
  1491  	}
  1492  	// Now make sure we have all peers.
  1493  	for leader != nil && time.Now().Before(expires) {
  1494  		if len(leader.JetStreamClusterPeers()) == numPeersExpected {
  1495  			time.Sleep(100 * time.Millisecond)
  1496  			return
  1497  		}
  1498  		time.Sleep(10 * time.Millisecond)
  1499  	}
  1500  
  1501  	if leader == nil {
  1502  		c.shutdown()
  1503  		c.t.Fatalf("Failed to elect a meta-leader")
  1504  	}
  1505  
  1506  	peersSeen := len(leader.JetStreamClusterPeers())
  1507  	c.shutdown()
  1508  
  1509  	if leader == nil {
  1510  		c.t.Fatalf("Expected a cluster leader and fully formed cluster, no leader")
  1511  	} else {
  1512  		c.t.Fatalf("Expected a fully formed cluster, only %d of %d peers seen", peersSeen, numPeersExpected)
  1513  	}
  1514  }
  1515  
  1516  // Helper function to remove JetStream from a server.
  1517  func (c *cluster) removeJetStream(s *Server) {
  1518  	c.t.Helper()
  1519  	index := -1
  1520  	for i, cs := range c.servers {
  1521  		if cs == s {
  1522  			index = i
  1523  			break
  1524  		}
  1525  	}
  1526  	cf := c.opts[index].ConfigFile
  1527  	cb, _ := os.ReadFile(cf)
  1528  	var sb strings.Builder
  1529  	for _, l := range strings.Split(string(cb), "\n") {
  1530  		if !strings.HasPrefix(strings.TrimSpace(l), "jetstream") {
  1531  			sb.WriteString(l + "\n")
  1532  		}
  1533  	}
  1534  	if err := os.WriteFile(cf, []byte(sb.String()), 0644); err != nil {
  1535  		c.t.Fatalf("Error writing updated config file: %v", err)
  1536  	}
  1537  	if err := s.Reload(); err != nil {
  1538  		c.t.Fatalf("Error on server reload: %v", err)
  1539  	}
  1540  	time.Sleep(100 * time.Millisecond)
  1541  }
  1542  
  1543  func (c *cluster) stopAll() {
  1544  	c.t.Helper()
  1545  	for _, s := range c.servers {
  1546  		s.Shutdown()
  1547  	}
  1548  }
  1549  
  1550  func (c *cluster) restartAll() {
  1551  	c.t.Helper()
  1552  	for i, s := range c.servers {
  1553  		if !s.Running() {
  1554  			opts := c.opts[i]
  1555  			s, o := RunServerWithConfig(opts.ConfigFile)
  1556  			c.servers[i] = s
  1557  			c.opts[i] = o
  1558  		}
  1559  	}
  1560  	c.waitOnClusterReady()
  1561  }
  1562  
  1563  func (c *cluster) lameDuckRestartAll() {
  1564  	c.t.Helper()
  1565  	for i, s := range c.servers {
  1566  		s.lameDuckMode()
  1567  		s.WaitForShutdown()
  1568  		if !s.Running() {
  1569  			opts := c.opts[i]
  1570  			s, o := RunServerWithConfig(opts.ConfigFile)
  1571  			c.servers[i] = s
  1572  			c.opts[i] = o
  1573  		}
  1574  	}
  1575  	c.waitOnClusterReady()
  1576  }
  1577  
  1578  func (c *cluster) restartAllSamePorts() {
  1579  	c.t.Helper()
  1580  	for i, s := range c.servers {
  1581  		if !s.Running() {
  1582  			opts := c.opts[i]
  1583  			s := RunServer(opts)
  1584  			c.servers[i] = s
  1585  		}
  1586  	}
  1587  	c.waitOnClusterReady()
  1588  }
  1589  
  1590  func (c *cluster) totalSubs() (total int) {
  1591  	c.t.Helper()
  1592  	for _, s := range c.servers {
  1593  		total += int(s.NumSubscriptions())
  1594  	}
  1595  	return total
  1596  }
  1597  
  1598  func (c *cluster) stableTotalSubs() (total int) {
  1599  	nsubs := -1
  1600  	checkFor(c.t, 2*time.Second, 250*time.Millisecond, func() error {
  1601  		subs := c.totalSubs()
  1602  		if subs == nsubs {
  1603  			return nil
  1604  		}
  1605  		nsubs = subs
  1606  		return fmt.Errorf("Still stabilizing")
  1607  	})
  1608  	return nsubs
  1609  
  1610  }
  1611  
  1612  func addStream(t *testing.T, nc *nats.Conn, cfg *StreamConfig) *StreamInfo {
  1613  	t.Helper()
  1614  	si, err := addStreamWithError(t, nc, cfg)
  1615  	if err != nil {
  1616  		t.Fatalf("Unexpected error: %+v", err)
  1617  	}
  1618  	return si
  1619  }
  1620  
  1621  func addStreamWithError(t *testing.T, nc *nats.Conn, cfg *StreamConfig) (*StreamInfo, *ApiError) {
  1622  	t.Helper()
  1623  	req, err := json.Marshal(cfg)
  1624  	require_NoError(t, err)
  1625  	rmsg, err := nc.Request(fmt.Sprintf(JSApiStreamCreateT, cfg.Name), req, 5*time.Second)
  1626  	require_NoError(t, err)
  1627  	var resp JSApiStreamCreateResponse
  1628  	err = json.Unmarshal(rmsg.Data, &resp)
  1629  	require_NoError(t, err)
  1630  	if resp.Type != JSApiStreamCreateResponseType {
  1631  		t.Fatalf("Invalid response type %s expected %s", resp.Type, JSApiStreamCreateResponseType)
  1632  	}
  1633  	return resp.StreamInfo, resp.Error
  1634  }
  1635  
  1636  func updateStream(t *testing.T, nc *nats.Conn, cfg *StreamConfig) *StreamInfo {
  1637  	t.Helper()
  1638  	req, err := json.Marshal(cfg)
  1639  	require_NoError(t, err)
  1640  	rmsg, err := nc.Request(fmt.Sprintf(JSApiStreamUpdateT, cfg.Name), req, time.Second)
  1641  	require_NoError(t, err)
  1642  	var resp JSApiStreamCreateResponse
  1643  	err = json.Unmarshal(rmsg.Data, &resp)
  1644  	require_NoError(t, err)
  1645  	if resp.Type != JSApiStreamUpdateResponseType {
  1646  		t.Fatalf("Invalid response type %s expected %s", resp.Type, JSApiStreamUpdateResponseType)
  1647  	}
  1648  	if resp.Error != nil {
  1649  		t.Fatalf("Unexpected error: %+v", resp.Error)
  1650  	}
  1651  	return resp.StreamInfo
  1652  }
  1653  
  1654  // setInActiveDeleteThreshold sets the delete threshold for how long to wait
  1655  // before deleting an inactive consumer.
  1656  func (o *consumer) setInActiveDeleteThreshold(dthresh time.Duration) error {
  1657  	o.mu.Lock()
  1658  	defer o.mu.Unlock()
  1659  
  1660  	deleteWasRunning := o.dtmr != nil
  1661  	stopAndClearTimer(&o.dtmr)
  1662  	// Do not add jitter if set via here.
  1663  	o.dthresh = dthresh
  1664  	if deleteWasRunning {
  1665  		o.dtmr = time.AfterFunc(o.dthresh, func() { o.deleteNotActive() })
  1666  	}
  1667  	return nil
  1668  }
  1669  
  1670  // Net Proxy - For introducing RTT and BW constraints.
  1671  type netProxy struct {
  1672  	sync.RWMutex
  1673  	listener net.Listener
  1674  	conns    []net.Conn
  1675  	rtt      time.Duration
  1676  	up       int
  1677  	down     int
  1678  	url      string
  1679  	surl     string
  1680  }
  1681  
  1682  func newNetProxy(rtt time.Duration, upRate, downRate int, serverURL string) *netProxy {
  1683  	return createNetProxy(rtt, upRate, downRate, serverURL, true)
  1684  }
  1685  
  1686  func createNetProxy(rtt time.Duration, upRate, downRate int, serverURL string, start bool) *netProxy {
  1687  	hp := net.JoinHostPort("127.0.0.1", "0")
  1688  	l, e := net.Listen("tcp", hp)
  1689  	if e != nil {
  1690  		panic(fmt.Sprintf("Error listening on port: %s, %q", hp, e))
  1691  	}
  1692  	port := l.Addr().(*net.TCPAddr).Port
  1693  	proxy := &netProxy{
  1694  		listener: l,
  1695  		rtt:      rtt,
  1696  		up:       upRate,
  1697  		down:     downRate,
  1698  		url:      fmt.Sprintf("nats://127.0.0.1:%d", port),
  1699  		surl:     serverURL,
  1700  	}
  1701  	if start {
  1702  		proxy.start()
  1703  	}
  1704  	return proxy
  1705  }
  1706  
  1707  func (np *netProxy) start() {
  1708  	u, err := url.Parse(np.surl)
  1709  	if err != nil {
  1710  		panic(fmt.Sprintf("Could not parse server URL: %v", err))
  1711  	}
  1712  	host := u.Host
  1713  
  1714  	go func() {
  1715  		for {
  1716  			client, err := np.listener.Accept()
  1717  			if err != nil {
  1718  				return
  1719  			}
  1720  			server, err := net.DialTimeout("tcp", host, time.Second)
  1721  			if err != nil {
  1722  				continue
  1723  			}
  1724  			np.conns = append(np.conns, client, server)
  1725  			go np.loop(np.up, client, server)
  1726  			go np.loop(np.down, server, client)
  1727  		}
  1728  	}()
  1729  }
  1730  
  1731  func (np *netProxy) clientURL() string {
  1732  	return np.url
  1733  }
  1734  
  1735  func (np *netProxy) routeURL() string {
  1736  	return strings.Replace(np.url, "nats", "nats-route", 1)
  1737  }
  1738  
  1739  func (np *netProxy) loop(tbw int, r, w net.Conn) {
  1740  	const rbl = 8192
  1741  	var buf [rbl]byte
  1742  	ctx := context.Background()
  1743  
  1744  	rl := rate.NewLimiter(rate.Limit(tbw), rbl)
  1745  
  1746  	for {
  1747  		n, err := r.Read(buf[:])
  1748  		if err != nil {
  1749  			w.Close()
  1750  			return
  1751  		}
  1752  		// RTT delays
  1753  		np.RLock()
  1754  		delay := np.rtt / 2
  1755  		np.RUnlock()
  1756  		if delay > 0 {
  1757  			time.Sleep(delay)
  1758  		}
  1759  		if err := rl.WaitN(ctx, n); err != nil {
  1760  			return
  1761  		}
  1762  		if _, err = w.Write(buf[:n]); err != nil {
  1763  			r.Close()
  1764  			return
  1765  		}
  1766  	}
  1767  }
  1768  
  1769  func (np *netProxy) updateRTT(rtt time.Duration) {
  1770  	np.Lock()
  1771  	np.rtt = rtt
  1772  	np.Unlock()
  1773  }
  1774  
  1775  func (np *netProxy) stop() {
  1776  	if np.listener != nil {
  1777  		np.listener.Close()
  1778  		np.listener = nil
  1779  		for _, c := range np.conns {
  1780  			c.Close()
  1781  		}
  1782  	}
  1783  }
  1784  
  1785  // Bitset, aka bitvector, allows tracking of large number of bits efficiently
  1786  type bitset struct {
  1787  	// Bit map storage
  1788  	bitmap []uint8
  1789  	// Number of bits currently set to 1
  1790  	currentCount uint64
  1791  	// Number of bits stored
  1792  	size uint64
  1793  }
  1794  
  1795  func NewBitset(size uint64) *bitset {
  1796  	byteSize := (size + 7) / 8 //Round up to the nearest byte
  1797  
  1798  	return &bitset{
  1799  		bitmap:       make([]uint8, int(byteSize)),
  1800  		size:         size,
  1801  		currentCount: 0,
  1802  	}
  1803  }
  1804  
  1805  func (b *bitset) get(index uint64) bool {
  1806  	if index >= b.size {
  1807  		panic(fmt.Sprintf("Index %d out of bounds, size %d", index, b.size))
  1808  	}
  1809  	byteIndex := index / 8
  1810  	bitIndex := uint(index % 8)
  1811  	bit := (b.bitmap[byteIndex] & (uint8(1) << bitIndex))
  1812  	return bit != 0
  1813  }
  1814  
  1815  func (b *bitset) set(index uint64, value bool) {
  1816  	if index >= b.size {
  1817  		panic(fmt.Sprintf("Index %d out of bounds, size %d", index, b.size))
  1818  	}
  1819  	byteIndex := index / 8
  1820  	bitIndex := uint(index % 8)
  1821  	byteMask := uint8(1) << bitIndex
  1822  	isSet := (b.bitmap[byteIndex] & (uint8(1) << bitIndex)) != 0
  1823  	if value {
  1824  		b.bitmap[byteIndex] |= byteMask
  1825  		if !isSet {
  1826  			b.currentCount += 1
  1827  		}
  1828  	} else {
  1829  		b.bitmap[byteIndex] &= ^byteMask
  1830  		if isSet {
  1831  			b.currentCount -= 1
  1832  		}
  1833  	}
  1834  }
  1835  
  1836  func (b *bitset) count() uint64 {
  1837  	return b.currentCount
  1838  }
  1839  
  1840  func (b *bitset) String() string {
  1841  	const block = 8 // 8 bytes, 64 bits per line
  1842  	sb := strings.Builder{}
  1843  
  1844  	sb.WriteString(fmt.Sprintf("Bits set: %d/%d\n", b.currentCount, b.size))
  1845  	for i := 0; i < len(b.bitmap); i++ {
  1846  		if i%block == 0 {
  1847  			if i > 0 {
  1848  				sb.WriteString("\n")
  1849  			}
  1850  			sb.WriteString(fmt.Sprintf("[%4d] ", i*8))
  1851  		}
  1852  		for j := uint8(0); j < 8; j++ {
  1853  			if b.bitmap[i]&(1<<j) > 0 {
  1854  				sb.WriteString("1")
  1855  			} else {
  1856  				sb.WriteString("0")
  1857  			}
  1858  		}
  1859  	}
  1860  	sb.WriteString("\n")
  1861  	return sb.String()
  1862  }