mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Merge pull request #2156 from nats-io/leaf-shuffle
[CHANGED] Randomize Leafnode remote URLs and add option to disable
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -133,6 +134,17 @@ func validateLeafNode(o *Options) error {
|
||||
if err := validateLeafNodeAuthOptions(o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rem := range o.LeafNode.Remotes {
|
||||
if rem.NoRandomize {
|
||||
continue
|
||||
}
|
||||
|
||||
rand.Shuffle(len(rem.URLs), func(i, j int) {
|
||||
rem.URLs[i], rem.URLs[j] = rem.URLs[j], rem.URLs[i]
|
||||
})
|
||||
}
|
||||
|
||||
// In local config mode, check that leafnode configuration refers to accounts that exist.
|
||||
if len(o.TrustedOperators) == 0 {
|
||||
accNames := map[string]struct{}{}
|
||||
|
||||
@@ -85,6 +85,59 @@ func TestLeafNodeRandomIP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeafNodeRandomRemotes(t *testing.T) {
|
||||
// 16! possible permutations.
|
||||
orderedURLs := make([]*url.URL, 0, 16)
|
||||
for i := 0; i < cap(orderedURLs); i++ {
|
||||
orderedURLs = append(orderedURLs, &url.URL{
|
||||
Scheme: "nats-leaf",
|
||||
Host: fmt.Sprintf("host%d:7422", i),
|
||||
})
|
||||
}
|
||||
|
||||
o := DefaultOptions()
|
||||
o.LeafNode.Remotes = []*RemoteLeafOpts{
|
||||
{NoRandomize: true},
|
||||
{NoRandomize: false},
|
||||
}
|
||||
o.LeafNode.Remotes[0].URLs = make([]*url.URL, cap(orderedURLs))
|
||||
copy(o.LeafNode.Remotes[0].URLs, orderedURLs)
|
||||
o.LeafNode.Remotes[1].URLs = make([]*url.URL, cap(orderedURLs))
|
||||
copy(o.LeafNode.Remotes[1].URLs, orderedURLs)
|
||||
|
||||
s := RunServer(o)
|
||||
s.Shutdown()
|
||||
|
||||
gotOrdered := o.LeafNode.Remotes[0].URLs
|
||||
if got, want := len(gotOrdered), len(orderedURLs); got != want {
|
||||
t.Fatalf("Unexpected rem0 len URLs, got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
// These should be IN order.
|
||||
for i := range orderedURLs {
|
||||
if got, want := gotOrdered[i].String(), orderedURLs[i].String(); got != want {
|
||||
t.Fatalf("Unexpected ordered url, got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
gotRandom := o.LeafNode.Remotes[1].URLs
|
||||
if got, want := len(gotRandom), len(orderedURLs); got != want {
|
||||
t.Fatalf("Unexpected rem1 len URLs, got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
// These should be OUT of order.
|
||||
var random bool
|
||||
for i := range orderedURLs {
|
||||
if gotRandom[i].String() != orderedURLs[i].String() {
|
||||
random = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !random {
|
||||
t.Fatal("Expected urls to be random")
|
||||
}
|
||||
}
|
||||
|
||||
type testLoopbackResolver struct{}
|
||||
|
||||
func (r *testLoopbackResolver) LookupHost(ctx context.Context, host string) ([]string, error) {
|
||||
@@ -3226,16 +3279,15 @@ func TestLeafNodeLoopDetectionWithMultipleClusters(t *testing.T) {
|
||||
|
||||
checkClusterFormed(t, l1, l2)
|
||||
|
||||
u1, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", lo1.LeafNode.Port))
|
||||
u2, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", lo2.LeafNode.Port))
|
||||
urls := []*url.URL{u1, u2}
|
||||
|
||||
ro1 := DefaultOptions()
|
||||
ro1.Cluster.Name = "remote"
|
||||
ro1.Cluster.Host = "127.0.0.1"
|
||||
ro1.Cluster.Port = -1
|
||||
ro1.LeafNode.ReconnectInterval = 50 * time.Millisecond
|
||||
ro1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: urls}}
|
||||
ro1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{
|
||||
{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo1.LeafNode.Port)},
|
||||
{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo2.LeafNode.Port)},
|
||||
}}}
|
||||
r1 := RunServer(ro1)
|
||||
defer r1.Shutdown()
|
||||
|
||||
@@ -3248,7 +3300,10 @@ func TestLeafNodeLoopDetectionWithMultipleClusters(t *testing.T) {
|
||||
ro2.Cluster.Port = -1
|
||||
ro2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", ro1.Cluster.Port))
|
||||
ro2.LeafNode.ReconnectInterval = 50 * time.Millisecond
|
||||
ro2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: urls}}
|
||||
ro2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{
|
||||
{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo1.LeafNode.Port)},
|
||||
{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo2.LeafNode.Port)},
|
||||
}}}
|
||||
r2 := RunServer(ro2)
|
||||
defer r2.Shutdown()
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ type LeafNodeOpts struct {
|
||||
// RemoteLeafOpts are options for connecting to a remote server as a leaf node.
|
||||
type RemoteLeafOpts struct {
|
||||
LocalAccount string `json:"local_account,omitempty"`
|
||||
NoRandomize bool `json:"-"`
|
||||
URLs []*url.URL `json:"urls,omitempty"`
|
||||
Credentials string `json:"-"`
|
||||
TLS bool `json:"-"`
|
||||
@@ -1761,6 +1762,8 @@ func parseRemoteLeafNodes(v interface{}, errors *[]error, warnings *[]error) ([]
|
||||
for k, v := range rm {
|
||||
tk, v = unwrapValue(v, <)
|
||||
switch strings.ToLower(k) {
|
||||
case "no_randomize", "dont_randomize":
|
||||
remote.NoRandomize = v.(bool)
|
||||
case "url", "urls":
|
||||
switch v := v.(type) {
|
||||
case []interface{}, []string:
|
||||
|
||||
@@ -16,6 +16,7 @@ package server
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -2383,6 +2384,66 @@ func TestParsingLeafNodeRemotes(t *testing.T) {
|
||||
t.Fatalf("Expected %v, got %v", expected, opts.LeafNode.Remotes[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("url ordering", func(t *testing.T) {
|
||||
// 16! possible permutations.
|
||||
orderedURLs := make([]string, 0, 16)
|
||||
for i := 0; i < cap(orderedURLs); i++ {
|
||||
orderedURLs = append(orderedURLs, fmt.Sprintf("nats-leaf://host%d:7422", i))
|
||||
}
|
||||
confURLs, err := json.Marshal(orderedURLs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
content := `
|
||||
leafnodes {
|
||||
remotes = [
|
||||
{
|
||||
dont_randomize: true
|
||||
urls: %[1]s
|
||||
}
|
||||
{
|
||||
urls: %[1]s
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf := createConfFile(t, []byte(fmt.Sprintf(content, confURLs)))
|
||||
defer removeFile(t, conf)
|
||||
|
||||
s, o := RunServerWithConfig(conf)
|
||||
s.Shutdown()
|
||||
|
||||
gotOrdered := o.LeafNode.Remotes[0].URLs
|
||||
if got, want := len(gotOrdered), len(orderedURLs); got != want {
|
||||
t.Fatalf("Unexpected rem0 len URLs, got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
// These should be IN order.
|
||||
for i := range orderedURLs {
|
||||
if got, want := gotOrdered[i].String(), orderedURLs[i]; got != want {
|
||||
t.Fatalf("Unexpected ordered url, got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
gotRandom := o.LeafNode.Remotes[1].URLs
|
||||
if got, want := len(gotRandom), len(orderedURLs); got != want {
|
||||
t.Fatalf("Unexpected rem1 len URLs, got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
// These should be OUT of order.
|
||||
var random bool
|
||||
for i := range orderedURLs {
|
||||
if gotRandom[i].String() != orderedURLs[i] {
|
||||
random = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !random {
|
||||
t.Fatal("Expected urls to be random")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLargeMaxControlLine(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user