github.com/mattyr/nomad@v0.3.3-0.20160919021406-3485a065154a/client/rpcproxy/rpcproxy_test.go (about)

     1  package rpcproxy
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"log"
     8  	"math/rand"
     9  	"net"
    10  	"os"
    11  	"strings"
    12  	"sync/atomic"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  const (
    18  	ipv4len          = 4
    19  	nodeNameFmt      = "s%03d"
    20  	defaultNomadPort = "4647"
    21  
    22  	// Poached from RFC2544 and RFC3330
    23  	testingNetworkCidr   = "198.18.0.0/15"
    24  	testingNetworkUint32 = 3323068416
    25  )
    26  
    27  var (
    28  	localLogger    *log.Logger
    29  	localLogBuffer *bytes.Buffer
    30  	serverCount    uint32
    31  	validIp        uint32
    32  )
    33  
    34  func init() {
    35  	localLogBuffer = new(bytes.Buffer)
    36  	localLogger = log.New(localLogBuffer, "", 0)
    37  }
    38  
    39  func makeServerEndpointName() string {
    40  	serverNum := atomic.AddUint32(&serverCount, 1)
    41  	validIp := testingNetworkUint32 + serverNum
    42  	ipv4 := make(net.IP, ipv4len)
    43  	binary.BigEndian.PutUint32(ipv4, validIp)
    44  	return net.JoinHostPort(ipv4.String(), defaultNomadPort)
    45  }
    46  
    47  func GetBufferedLogger() *log.Logger {
    48  	return localLogger
    49  }
    50  
    51  type fauxConnPool struct {
    52  	// failPct between 0.0 and 1.0 == pct of time a Ping should fail
    53  	failPct float64
    54  }
    55  
    56  func (cp *fauxConnPool) PingNomadServer(region string, majorVersion int, s *ServerEndpoint) (bool, error) {
    57  	var success bool
    58  	successProb := rand.Float64()
    59  	if successProb > cp.failPct {
    60  		success = true
    61  	}
    62  	return success, nil
    63  }
    64  
    65  type fauxSerf struct {
    66  	datacenter      string
    67  	numNodes        int
    68  	region          string
    69  	rpcMinorVersion int
    70  	rpcMajorVersion int
    71  }
    72  
    73  func (s *fauxSerf) NumNodes() int {
    74  	return s.numNodes
    75  }
    76  
    77  func (s *fauxSerf) Region() string {
    78  	return s.region
    79  }
    80  
    81  func (s *fauxSerf) Datacenter() string {
    82  	return s.datacenter
    83  }
    84  
    85  func (s *fauxSerf) RPCMajorVersion() int {
    86  	return s.rpcMajorVersion
    87  }
    88  
    89  func (s *fauxSerf) RPCMinorVersion() int {
    90  	return s.rpcMinorVersion
    91  }
    92  
    93  func testRPCProxy() (p *RPCProxy) {
    94  	logger := GetBufferedLogger()
    95  	logger = log.New(os.Stderr, "", log.LstdFlags)
    96  	shutdownCh := make(chan struct{})
    97  	p = NewRPCProxy(logger, shutdownCh, &fauxSerf{numNodes: 16384}, &fauxConnPool{})
    98  	return p
    99  }
   100  
   101  func testRPCProxyFailProb(failPct float64) (p *RPCProxy) {
   102  	logger := GetBufferedLogger()
   103  	logger = log.New(os.Stderr, "", log.LstdFlags)
   104  	shutdownCh := make(chan struct{})
   105  	p = NewRPCProxy(logger, shutdownCh, &fauxSerf{}, &fauxConnPool{failPct: failPct})
   106  	return p
   107  }
   108  
   109  // func (p *RPCProxy) AddPrimaryServer(server *ServerEndpoint) {
   110  func TestRPCProxy_AddPrimaryServer(t *testing.T) {
   111  	p := testRPCProxy()
   112  	var num int
   113  	num = p.NumServers()
   114  	if num != 0 {
   115  		t.Fatalf("Expected zero servers to start")
   116  	}
   117  
   118  	s1Endpoint := makeServerEndpointName()
   119  	s1 := p.AddPrimaryServer(s1Endpoint)
   120  	num = p.NumServers()
   121  	if num != 1 {
   122  		t.Fatalf("Expected one server")
   123  	}
   124  	if s1 == nil {
   125  		t.Fatalf("bad")
   126  	}
   127  	if s1.Name != s1Endpoint {
   128  		t.Fatalf("bad")
   129  	}
   130  
   131  	s1 = p.AddPrimaryServer(s1Endpoint)
   132  	num = p.NumServers()
   133  	if num != 1 {
   134  		t.Fatalf("Expected one server (still)")
   135  	}
   136  	if s1 == nil {
   137  		t.Fatalf("bad")
   138  	}
   139  	if s1.Name != s1Endpoint {
   140  		t.Fatalf("bad")
   141  	}
   142  
   143  	s2Endpoint := makeServerEndpointName()
   144  	s2 := p.AddPrimaryServer(s2Endpoint)
   145  	num = p.NumServers()
   146  	if num != 2 {
   147  		t.Fatalf("Expected two servers")
   148  	}
   149  	if s2 == nil {
   150  		t.Fatalf("bad")
   151  	}
   152  	if s2.Name != s2Endpoint {
   153  		t.Fatalf("bad")
   154  	}
   155  }
   156  
   157  // func (p *RPCProxy) FindServer() (server *ServerEndpoint) {
   158  func TestRPCProxy_FindServer(t *testing.T) {
   159  	p := testRPCProxy()
   160  
   161  	if p.FindServer() != nil {
   162  		t.Fatalf("Expected nil return")
   163  	}
   164  
   165  	s1Endpoint := makeServerEndpointName()
   166  	p.AddPrimaryServer(s1Endpoint)
   167  	if p.NumServers() != 1 {
   168  		t.Fatalf("Expected one server")
   169  	}
   170  
   171  	s1 := p.FindServer()
   172  	if s1 == nil {
   173  		t.Fatalf("Expected non-nil server")
   174  	}
   175  	if s1.Name != s1Endpoint {
   176  		t.Fatalf("Expected s1 server")
   177  	}
   178  
   179  	s1 = p.FindServer()
   180  	if s1 == nil || s1.Name != s1Endpoint {
   181  		t.Fatalf("Expected s1 server (still)")
   182  	}
   183  
   184  	s2Endpoint := makeServerEndpointName()
   185  	p.AddPrimaryServer(s2Endpoint)
   186  	if p.NumServers() != 2 {
   187  		t.Fatalf("Expected two servers")
   188  	}
   189  	s1 = p.FindServer()
   190  	if s1 == nil || s1.Name != s1Endpoint {
   191  		t.Fatalf("Expected s1 server (still)")
   192  	}
   193  
   194  	p.NotifyFailedServer(s1)
   195  	s2 := p.FindServer()
   196  	if s2 == nil || s2.Name != s2Endpoint {
   197  		t.Fatalf("Expected s2 server")
   198  	}
   199  
   200  	p.NotifyFailedServer(s2)
   201  	s1 = p.FindServer()
   202  	if s1 == nil || s1.Name != s1Endpoint {
   203  		t.Fatalf("Expected s1 server")
   204  	}
   205  }
   206  
   207  // func New(logger *log.Logger, shutdownCh chan struct{}) (p *RPCProxy) {
   208  func TestRPCProxy_New(t *testing.T) {
   209  	logger := GetBufferedLogger()
   210  	logger = log.New(os.Stderr, "", log.LstdFlags)
   211  	shutdownCh := make(chan struct{})
   212  	p := NewRPCProxy(logger, shutdownCh, &fauxSerf{}, &fauxConnPool{})
   213  	if p == nil {
   214  		t.Fatalf("RPCProxy nil")
   215  	}
   216  }
   217  
   218  // func (p *RPCProxy) NotifyFailedServer(server *ServerEndpoint) {
   219  func TestRPCProxy_NotifyFailedServer(t *testing.T) {
   220  	p := testRPCProxy()
   221  
   222  	if p.NumServers() != 0 {
   223  		t.Fatalf("Expected zero servers to start")
   224  	}
   225  
   226  	// Try notifying for a server that is not managed by RPCProxy
   227  	s1Endpoint := makeServerEndpointName()
   228  	s1 := p.AddPrimaryServer(s1Endpoint)
   229  	if s1 == nil {
   230  		t.Fatalf("bad")
   231  	}
   232  	if p.NumServers() != 1 {
   233  		t.Fatalf("bad")
   234  	}
   235  	p.RemoveServer(s1)
   236  	if p.NumServers() != 0 {
   237  		t.Fatalf("bad")
   238  	}
   239  	p.NotifyFailedServer(s1)
   240  	s1 = p.AddPrimaryServer(s1Endpoint)
   241  
   242  	// Test again w/ a server not in the list
   243  	s2Endpoint := makeServerEndpointName()
   244  	s2 := p.AddPrimaryServer(s2Endpoint)
   245  	if s2 == nil {
   246  		t.Fatalf("bad")
   247  	}
   248  	if p.NumServers() != 2 {
   249  		t.Fatalf("bad")
   250  	}
   251  	p.RemoveServer(s2)
   252  	if p.NumServers() != 1 {
   253  		t.Fatalf("bad")
   254  	}
   255  	p.NotifyFailedServer(s2)
   256  	if p.NumServers() != 1 {
   257  		t.Fatalf("Expected one server")
   258  	}
   259  
   260  	// Re-add s2 so there are two servers in the RPCProxy server list
   261  	s2 = p.AddPrimaryServer(s2Endpoint)
   262  	if p.NumServers() != 2 {
   263  		t.Fatalf("Expected two servers")
   264  	}
   265  
   266  	// Find the first server, it should be s1
   267  	s1 = p.FindServer()
   268  	if s1 == nil || s1.Name != s1Endpoint {
   269  		t.Fatalf("Expected s1 server")
   270  	}
   271  
   272  	// Notify s2 as failed, s1 should still be first
   273  	p.NotifyFailedServer(s2)
   274  	s1 = p.FindServer()
   275  	if s1 == nil || s1.Name != s1Endpoint {
   276  		t.Fatalf("Expected s1 server (still)")
   277  	}
   278  
   279  	// Fail s1, s2 should be first
   280  	p.NotifyFailedServer(s1)
   281  	s2 = p.FindServer()
   282  	if s2 == nil || s2.Name != s2Endpoint {
   283  		t.Fatalf("Expected s2 server")
   284  	}
   285  
   286  	// Fail s2, s1 should be first
   287  	p.NotifyFailedServer(s2)
   288  	s1 = p.FindServer()
   289  	if s1 == nil || s1.Name != s1Endpoint {
   290  		t.Fatalf("Expected s1 server")
   291  	}
   292  }
   293  
   294  // func (p *RPCProxy) NumServers() (numServers int) {
   295  func TestRPCProxy_NumServers(t *testing.T) {
   296  	p := testRPCProxy()
   297  	const maxNumServers = 100
   298  	serverList := make([]*ServerEndpoint, 0, maxNumServers)
   299  
   300  	// Add some servers
   301  	for i := 0; i < maxNumServers; i++ {
   302  		num := p.NumServers()
   303  		if num != i {
   304  			t.Fatalf("%d: Expected %d servers", i, num)
   305  		}
   306  		serverName := makeServerEndpointName()
   307  		s := p.AddPrimaryServer(serverName)
   308  		if s == nil {
   309  			t.Fatalf("Expected server from %+q", serverName)
   310  		}
   311  		serverList = append(serverList, s)
   312  
   313  		num = p.NumServers()
   314  		if num != i+1 {
   315  			t.Fatalf("%d: Expected %d servers", i, num+1)
   316  		}
   317  	}
   318  
   319  	// Remove some servers
   320  	for i := maxNumServers; i > 0; i-- {
   321  		num := p.NumServers()
   322  		if num != i {
   323  			t.Fatalf("%d: Expected %d servers", i, num)
   324  		}
   325  		p.RemoveServer(serverList[i-1])
   326  		num = p.NumServers()
   327  		if num != i-1 {
   328  			t.Fatalf("%d: Expected %d servers", i, num-1)
   329  		}
   330  	}
   331  }
   332  
   333  // func (p *RPCProxy) RebalanceServers() {
   334  func TestRPCProxy_RebalanceServers(t *testing.T) {
   335  	const failPct = 0.5
   336  	p := testRPCProxyFailProb(failPct)
   337  	const maxServers = 100
   338  	const numShuffleTests = 100
   339  	const uniquePassRate = 0.5
   340  
   341  	// Make a huge list of nodes.
   342  	for i := 0; i < maxServers; i++ {
   343  		p.AddPrimaryServer(makeServerEndpointName())
   344  	}
   345  
   346  	// Keep track of how many unique shuffles we get.
   347  	uniques := make(map[string]struct{}, maxServers)
   348  	for i := 0; i < numShuffleTests; i++ {
   349  		p.RebalanceServers()
   350  
   351  		var names []string
   352  		for j := 0; j < maxServers; j++ {
   353  			server := p.FindServer()
   354  			p.NotifyFailedServer(server)
   355  			names = append(names, server.Name)
   356  		}
   357  		key := strings.Join(names, "|")
   358  		uniques[key] = struct{}{}
   359  	}
   360  
   361  	// We have to allow for the fact that there won't always be a unique
   362  	// shuffle each pass, so we just look for smell here without the test
   363  	// being flaky.
   364  	if len(uniques) < int(maxServers*uniquePassRate) {
   365  		t.Fatalf("unique shuffle ratio too low: %d/%d", len(uniques), maxServers)
   366  	}
   367  }
   368  
   369  // func (p *RPCProxy) RemoveServer(server *ServerEndpoint) {
   370  func TestRPCProxy_RemoveServer(t *testing.T) {
   371  	p := testRPCProxy()
   372  	if p.NumServers() != 0 {
   373  		t.Fatalf("Expected zero servers to start")
   374  	}
   375  
   376  	// Test removing server before its added
   377  	s1Endpoint := makeServerEndpointName()
   378  	s1 := p.AddPrimaryServer(s1Endpoint)
   379  	if p.NumServers() != 1 {
   380  		t.Fatalf("bad")
   381  	}
   382  	if s1 == nil || s1.Name != s1Endpoint {
   383  		t.Fatalf("Expected s1 server: %+q", s1.Name)
   384  	}
   385  	s1 = p.FindServer()
   386  	if s1 == nil || s1.Name != s1Endpoint {
   387  		t.Fatalf("Expected s1 server: %+q", s1.Name)
   388  	}
   389  	p.RemoveServer(s1)
   390  	if p.NumServers() != 0 {
   391  		t.Fatalf("bad")
   392  	}
   393  	// Remove it a second time now that it doesn't exist
   394  	p.RemoveServer(s1)
   395  	if p.NumServers() != 0 {
   396  		t.Fatalf("bad")
   397  	}
   398  	p.AddPrimaryServer(s1Endpoint)
   399  	if p.NumServers() != 1 {
   400  		t.Fatalf("bad")
   401  	}
   402  
   403  	s2Endpoint := makeServerEndpointName()
   404  	s2 := p.AddPrimaryServer(s2Endpoint)
   405  	if p.NumServers() != 2 {
   406  		t.Fatalf("bad")
   407  	}
   408  	if s2 == nil || s2.Name != s2Endpoint {
   409  		t.Fatalf("Expected s2 server: %+q", s2.Name)
   410  	}
   411  	s1 = p.FindServer()
   412  	if s1 == nil || s1.Name != s1Endpoint {
   413  		t.Fatalf("Expected s1 to be the front of the list: %+q==%+q", s1.Name, s1Endpoint)
   414  	}
   415  	// Move s1 to the back of the server list
   416  	p.NotifyFailedServer(s1)
   417  	s2 = p.FindServer()
   418  	if s2 == nil || s2.Name != s2Endpoint {
   419  		t.Fatalf("Expected s2 server: %+q", s2Endpoint)
   420  	}
   421  	p.RemoveServer(s2)
   422  	if p.NumServers() != 1 {
   423  		t.Fatalf("bad")
   424  	}
   425  	p.RemoveServer(s2)
   426  	if p.NumServers() != 1 {
   427  		t.Fatalf("bad")
   428  	}
   429  	p.AddPrimaryServer(s2Endpoint)
   430  
   431  	const maxServers = 19
   432  	servers := make([]*ServerEndpoint, 0, maxServers)
   433  	servers = append(servers, s1)
   434  	servers = append(servers, s2)
   435  	// Already added two servers above
   436  	for i := maxServers; i > 2; i-- {
   437  		server := p.AddPrimaryServer(makeServerEndpointName())
   438  		servers = append(servers, server)
   439  	}
   440  	if p.NumServers() != maxServers {
   441  		t.Fatalf("Expected %d servers, received %d", maxServers, p.NumServers())
   442  	}
   443  
   444  	p.RebalanceServers()
   445  
   446  	if p.NumServers() != maxServers {
   447  		t.Fatalf("Expected %d servers, received %d", maxServers, p.NumServers())
   448  	}
   449  
   450  	findServer := func(server *ServerEndpoint) bool {
   451  		for i := p.NumServers(); i > 0; i-- {
   452  			s := p.FindServer()
   453  			if s == server {
   454  				return true
   455  			}
   456  		}
   457  		return false
   458  	}
   459  
   460  	expectedNumServers := maxServers
   461  	removedServers := make([]*ServerEndpoint, 0, maxServers)
   462  
   463  	// Remove servers from the front of the list
   464  	for i := 3; i > 0; i-- {
   465  		server := p.FindServer()
   466  		if server == nil {
   467  			t.Fatalf("FindServer returned nil")
   468  		}
   469  		p.RemoveServer(server)
   470  		expectedNumServers--
   471  		if p.NumServers() != expectedNumServers {
   472  			t.Fatalf("Expected %d servers (got %d)", expectedNumServers, p.NumServers())
   473  		}
   474  		if findServer(server) == true {
   475  			t.Fatalf("Did not expect to find server %s after removal from the front", server.Name)
   476  		}
   477  		removedServers = append(removedServers, server)
   478  	}
   479  
   480  	// Remove server from the end of the list
   481  	for i := 3; i > 0; i-- {
   482  		server := p.FindServer()
   483  		p.NotifyFailedServer(server)
   484  		p.RemoveServer(server)
   485  		expectedNumServers--
   486  		if p.NumServers() != expectedNumServers {
   487  			t.Fatalf("Expected %d servers (got %d)", expectedNumServers, p.NumServers())
   488  		}
   489  		if findServer(server) == true {
   490  			t.Fatalf("Did not expect to find server %s", server.Name)
   491  		}
   492  		removedServers = append(removedServers, server)
   493  	}
   494  
   495  	// Remove server from the middle of the list
   496  	for i := 3; i > 0; i-- {
   497  		server := p.FindServer()
   498  		p.NotifyFailedServer(server)
   499  		server2 := p.FindServer()
   500  		p.NotifyFailedServer(server2) // server2 now at end of the list
   501  
   502  		p.RemoveServer(server)
   503  		expectedNumServers--
   504  		if p.NumServers() != expectedNumServers {
   505  			t.Fatalf("Expected %d servers (got %d)", expectedNumServers, p.NumServers())
   506  		}
   507  		if findServer(server) == true {
   508  			t.Fatalf("Did not expect to find server %s", server.Name)
   509  		}
   510  		removedServers = append(removedServers, server)
   511  	}
   512  
   513  	if p.NumServers()+len(removedServers) != maxServers {
   514  		t.Fatalf("Expected %d+%d=%d servers", p.NumServers(), len(removedServers), maxServers)
   515  	}
   516  
   517  	// Drain the remaining servers from the middle
   518  	for i := p.NumServers(); i > 0; i-- {
   519  		server := p.FindServer()
   520  		p.NotifyFailedServer(server)
   521  		server2 := p.FindServer()
   522  		p.NotifyFailedServer(server2) // server2 now at end of the list
   523  		p.RemoveServer(server)
   524  		removedServers = append(removedServers, server)
   525  	}
   526  
   527  	if p.NumServers() != 0 {
   528  		t.Fatalf("Expected an empty server list")
   529  	}
   530  	if len(removedServers) != maxServers {
   531  		t.Fatalf("Expected all servers to be in removed server list")
   532  	}
   533  }
   534  
   535  // func (p *RPCProxy) Start() {
   536  
   537  // func (l *serverList) cycleServer() (servers []*Server) {
   538  func TestRPCProxyInternal_cycleServer(t *testing.T) {
   539  	p := testRPCProxy()
   540  	l := p.getServerList()
   541  
   542  	server0 := &ServerEndpoint{Name: "server1"}
   543  	server1 := &ServerEndpoint{Name: "server2"}
   544  	server2 := &ServerEndpoint{Name: "server3"}
   545  	l.L = append(l.L, server0, server1, server2)
   546  	p.saveServerList(l)
   547  
   548  	l = p.getServerList()
   549  	if len(l.L) != 3 {
   550  		t.Fatalf("server length incorrect: %d/3", len(l.L))
   551  	}
   552  	if l.L[0] != server0 &&
   553  		l.L[1] != server1 &&
   554  		l.L[2] != server2 {
   555  		t.Fatalf("initial server ordering not correct")
   556  	}
   557  
   558  	l.L = l.cycleServer()
   559  	if len(l.L) != 3 {
   560  		t.Fatalf("server length incorrect: %d/3", len(l.L))
   561  	}
   562  	if l.L[0] != server1 &&
   563  		l.L[1] != server2 &&
   564  		l.L[2] != server0 {
   565  		t.Fatalf("server ordering after one cycle not correct")
   566  	}
   567  
   568  	l.L = l.cycleServer()
   569  	if len(l.L) != 3 {
   570  		t.Fatalf("server length incorrect: %d/3", len(l.L))
   571  	}
   572  	if l.L[0] != server2 &&
   573  		l.L[1] != server0 &&
   574  		l.L[2] != server1 {
   575  		t.Fatalf("server ordering after two cycles not correct")
   576  	}
   577  
   578  	l.L = l.cycleServer()
   579  	if len(l.L) != 3 {
   580  		t.Fatalf("server length incorrect: %d/3", len(l.L))
   581  	}
   582  	if l.L[0] != server0 &&
   583  		l.L[1] != server1 &&
   584  		l.L[2] != server2 {
   585  		t.Fatalf("server ordering after three cycles not correct")
   586  	}
   587  }
   588  
   589  // func (p *RPCProxy) getServerList() serverList {
   590  func TestRPCProxyInternal_getServerList(t *testing.T) {
   591  	p := testRPCProxy()
   592  	l := p.getServerList()
   593  	if l.L == nil {
   594  		t.Fatalf("serverList.servers nil")
   595  	}
   596  
   597  	if len(l.L) != 0 {
   598  		t.Fatalf("serverList.servers length not zero")
   599  	}
   600  }
   601  
   602  func TestRPCProxyInternal_New(t *testing.T) {
   603  	p := testRPCProxy()
   604  	if p == nil {
   605  		t.Fatalf("bad")
   606  	}
   607  
   608  	if p.logger == nil {
   609  		t.Fatalf("bad")
   610  	}
   611  
   612  	if p.shutdownCh == nil {
   613  		t.Fatalf("bad")
   614  	}
   615  }
   616  
   617  // func (p *RPCProxy) reconcileServerList(l *serverList) bool {
   618  func TestRPCProxyInternal_reconcileServerList(t *testing.T) {
   619  	tests := []int{0, 1, 2, 3, 4, 5, 10, 100}
   620  	for _, n := range tests {
   621  		ok, err := test_reconcileServerList(n)
   622  		if !ok {
   623  			t.Errorf("Expected %d to pass: %v", n, err)
   624  		}
   625  	}
   626  }
   627  
   628  func test_reconcileServerList(maxServers int) (bool, error) {
   629  	// Build a server list, reconcile, verify the missing servers are
   630  	// missing, the added have been added, and the original server is
   631  	// present.
   632  	const failPct = 0.5
   633  	p := testRPCProxyFailProb(failPct)
   634  
   635  	var failedServers, healthyServers []*ServerEndpoint
   636  	for i := 0; i < maxServers; i++ {
   637  		nodeName := fmt.Sprintf("s%02d", i)
   638  
   639  		node := &ServerEndpoint{Name: nodeName}
   640  		// Add 66% of servers to RPCProxy
   641  		if rand.Float64() > 0.33 {
   642  			p.activateEndpoint(node)
   643  
   644  			// Of healthy servers, (ab)use connPoolPinger to
   645  			// failPct of the servers for the reconcile.  This
   646  			// allows for the selected server to no longer be
   647  			// healthy for the reconcile below.
   648  			if ok, _ := p.connPoolPinger.PingNomadServer(p.configInfo.Region(), p.configInfo.RPCMajorVersion(), node); ok {
   649  				// Will still be present
   650  				healthyServers = append(healthyServers, node)
   651  			} else {
   652  				// Will be missing
   653  				failedServers = append(failedServers, node)
   654  			}
   655  		} else {
   656  			// Will be added from the call to reconcile
   657  			healthyServers = append(healthyServers, node)
   658  		}
   659  	}
   660  
   661  	// Randomize RPCProxy's server list
   662  	p.RebalanceServers()
   663  	selectedServer := p.FindServer()
   664  
   665  	var selectedServerFailed bool
   666  	for _, s := range failedServers {
   667  		if selectedServer.Key().Equal(s.Key()) {
   668  			selectedServerFailed = true
   669  			break
   670  		}
   671  	}
   672  
   673  	// Update RPCProxy's server list to be "healthy" based on Serf.
   674  	// Reconcile this with origServers, which is shuffled and has a live
   675  	// connection, but possibly out of date.
   676  	origServers := p.getServerList()
   677  	p.saveServerList(serverList{L: healthyServers})
   678  
   679  	// This should always succeed with non-zero server lists
   680  	if !selectedServerFailed && !p.reconcileServerList(&origServers) &&
   681  		len(p.getServerList().L) != 0 &&
   682  		len(origServers.L) != 0 {
   683  		// If the random gods are unfavorable and we end up with zero
   684  		// length lists, expect things to fail and retry the test.
   685  		return false, fmt.Errorf("Expected reconcile to succeed: %v %d %d",
   686  			selectedServerFailed,
   687  			len(p.getServerList().L),
   688  			len(origServers.L))
   689  	}
   690  
   691  	// If we have zero-length server lists, test succeeded in degenerate
   692  	// case.
   693  	if len(p.getServerList().L) == 0 &&
   694  		len(origServers.L) == 0 {
   695  		// Failed as expected w/ zero length list
   696  		return true, nil
   697  	}
   698  
   699  	resultingServerMap := make(map[EndpointKey]bool)
   700  	for _, s := range p.getServerList().L {
   701  		resultingServerMap[*s.Key()] = true
   702  	}
   703  
   704  	// Test to make sure no failed servers are in the RPCProxy's
   705  	// list.  Error if there are any failedServers in l.servers
   706  	for _, s := range failedServers {
   707  		_, ok := resultingServerMap[*s.Key()]
   708  		if ok {
   709  			return false, fmt.Errorf("Found failed server %v in merged list %v", s, resultingServerMap)
   710  		}
   711  	}
   712  
   713  	// Test to make sure all healthy servers are in the healthy list.
   714  	if len(healthyServers) != len(p.getServerList().L) {
   715  		return false, fmt.Errorf("Expected healthy map and servers to match: %d/%d", len(healthyServers), len(healthyServers))
   716  	}
   717  
   718  	// Test to make sure all healthy servers are in the resultingServerMap list.
   719  	for _, s := range healthyServers {
   720  		_, ok := resultingServerMap[*s.Key()]
   721  		if !ok {
   722  			return false, fmt.Errorf("Server %v missing from healthy map after merged lists", s)
   723  		}
   724  	}
   725  	return true, nil
   726  }
   727  
   728  // func (l *serverList) refreshServerRebalanceTimer() {
   729  func TestRPCProxyInternal_refreshServerRebalanceTimer(t *testing.T) {
   730  	type clusterSizes struct {
   731  		numNodes     int
   732  		numServers   int
   733  		minRebalance time.Duration
   734  	}
   735  	clusters := []clusterSizes{
   736  		{0, 3, 10 * time.Minute},
   737  		{1, 0, 10 * time.Minute}, // partitioned cluster
   738  		{1, 3, 10 * time.Minute},
   739  		{2, 3, 10 * time.Minute},
   740  		{100, 0, 10 * time.Minute}, // partitioned
   741  		{100, 1, 10 * time.Minute}, // partitioned
   742  		{100, 3, 10 * time.Minute},
   743  		{1024, 1, 10 * time.Minute}, // partitioned
   744  		{1024, 3, 10 * time.Minute}, // partitioned
   745  		{1024, 5, 10 * time.Minute},
   746  		{16384, 1, 10 * time.Minute}, // partitioned
   747  		{16384, 2, 10 * time.Minute}, // partitioned
   748  		{16384, 3, 10 * time.Minute}, // partitioned
   749  		{16384, 5, 10 * time.Minute},
   750  		{65535, 0, 10 * time.Minute}, // partitioned
   751  		{65535, 1, 10 * time.Minute}, // partitioned
   752  		{65535, 2, 10 * time.Minute}, // partitioned
   753  		{65535, 3, 10 * time.Minute}, // partitioned
   754  		{65535, 5, 10 * time.Minute}, // partitioned
   755  		{65535, 7, 10 * time.Minute},
   756  		{1000000, 1, 10 * time.Minute},  // partitioned
   757  		{1000000, 2, 10 * time.Minute},  // partitioned
   758  		{1000000, 3, 10 * time.Minute},  // partitioned
   759  		{1000000, 5, 10 * time.Minute},  // partitioned
   760  		{1000000, 11, 10 * time.Minute}, // partitioned
   761  		{1000000, 19, 10 * time.Minute},
   762  	}
   763  
   764  	logger := log.New(os.Stderr, "", log.LstdFlags)
   765  	shutdownCh := make(chan struct{})
   766  
   767  	for i, s := range clusters {
   768  		p := NewRPCProxy(logger, shutdownCh, &fauxSerf{numNodes: s.numNodes}, &fauxConnPool{})
   769  		for i := 0; i < s.numServers; i++ {
   770  			nodeName := fmt.Sprintf("s%02d", i)
   771  			p.activateEndpoint(&ServerEndpoint{Name: nodeName})
   772  		}
   773  
   774  		d := p.refreshServerRebalanceTimer()
   775  		if d < s.minRebalance {
   776  			t.Errorf("[%d] duration too short for cluster of size %d and %d servers (%s < %s)", i, s.numNodes, s.numServers, d, s.minRebalance)
   777  		}
   778  	}
   779  }
   780  
   781  // func (p *RPCProxy) saveServerList(l serverList) {
   782  func TestRPCProxyInternal_saveServerList(t *testing.T) {
   783  	p := testRPCProxy()
   784  
   785  	// Initial condition
   786  	func() {
   787  		l := p.getServerList()
   788  		if len(l.L) != 0 {
   789  			t.Fatalf("RPCProxy.saveServerList failed to load init config")
   790  		}
   791  
   792  		newServer := new(ServerEndpoint)
   793  		l.L = append(l.L, newServer)
   794  		p.saveServerList(l)
   795  	}()
   796  
   797  	// Test that save works
   798  	func() {
   799  		l1 := p.getServerList()
   800  		t1NumServers := len(l1.L)
   801  		if t1NumServers != 1 {
   802  			t.Fatalf("RPCProxy.saveServerList failed to save mutated config")
   803  		}
   804  	}()
   805  
   806  	// Verify mutation w/o a save doesn't alter the original
   807  	func() {
   808  		newServer := new(ServerEndpoint)
   809  		l := p.getServerList()
   810  		l.L = append(l.L, newServer)
   811  
   812  		l_orig := p.getServerList()
   813  		origNumServers := len(l_orig.L)
   814  		if origNumServers >= len(l.L) {
   815  			t.Fatalf("RPCProxy.saveServerList unsaved config overwrote original")
   816  		}
   817  	}()
   818  }