...

Source file src/go.etcd.io/etcd/raft/v3/confchange/quick_test.go

Documentation: go.etcd.io/etcd/raft/v3/confchange

     1  // Copyright 2019 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package confchange
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"reflect"
    21  	"testing"
    22  	"testing/quick"
    23  
    24  	pb "go.etcd.io/etcd/raft/v3/raftpb"
    25  	"go.etcd.io/etcd/raft/v3/tracker"
    26  )
    27  
    28  // TestConfChangeQuick uses quickcheck to verify that simple and joint config
    29  // changes arrive at the same result.
    30  func TestConfChangeQuick(t *testing.T) {
    31  	cfg := &quick.Config{
    32  		MaxCount: 1000,
    33  	}
    34  
    35  	// Log the first couple of runs to give some indication of things working
    36  	// as intended.
    37  	const infoCount = 5
    38  
    39  	runWithJoint := func(c *Changer, ccs []pb.ConfChangeSingle) error {
    40  		cfg, prs, err := c.EnterJoint(false /* autoLeave */, ccs...)
    41  		if err != nil {
    42  			return err
    43  		}
    44  		// Also do this with autoLeave on, just to check that we'd get the same
    45  		// result.
    46  		cfg2a, prs2a, err := c.EnterJoint(true /* autoLeave */, ccs...)
    47  		if err != nil {
    48  			return err
    49  		}
    50  		cfg2a.AutoLeave = false
    51  		if !reflect.DeepEqual(cfg, cfg2a) || !reflect.DeepEqual(prs, prs2a) {
    52  			return fmt.Errorf("cfg: %+v\ncfg2a: %+v\nprs: %+v\nprs2a: %+v",
    53  				cfg, cfg2a, prs, prs2a)
    54  		}
    55  		c.Tracker.Config = cfg
    56  		c.Tracker.Progress = prs
    57  		cfg2b, prs2b, err := c.LeaveJoint()
    58  		if err != nil {
    59  			return err
    60  		}
    61  		// Reset back to the main branch with autoLeave=false.
    62  		c.Tracker.Config = cfg
    63  		c.Tracker.Progress = prs
    64  		cfg, prs, err = c.LeaveJoint()
    65  		if err != nil {
    66  			return err
    67  		}
    68  		if !reflect.DeepEqual(cfg, cfg2b) || !reflect.DeepEqual(prs, prs2b) {
    69  			return fmt.Errorf("cfg: %+v\ncfg2b: %+v\nprs: %+v\nprs2b: %+v",
    70  				cfg, cfg2b, prs, prs2b)
    71  		}
    72  		c.Tracker.Config = cfg
    73  		c.Tracker.Progress = prs
    74  		return nil
    75  	}
    76  
    77  	runWithSimple := func(c *Changer, ccs []pb.ConfChangeSingle) error {
    78  		for _, cc := range ccs {
    79  			cfg, prs, err := c.Simple(cc)
    80  			if err != nil {
    81  				return err
    82  			}
    83  			c.Tracker.Config, c.Tracker.Progress = cfg, prs
    84  		}
    85  		return nil
    86  	}
    87  
    88  	type testFunc func(*Changer, []pb.ConfChangeSingle) error
    89  
    90  	wrapper := func(invoke testFunc) func(setup initialChanges, ccs confChanges) (*Changer, error) {
    91  		return func(setup initialChanges, ccs confChanges) (*Changer, error) {
    92  			tr := tracker.MakeProgressTracker(10)
    93  			c := &Changer{
    94  				Tracker:   tr,
    95  				LastIndex: 10,
    96  			}
    97  
    98  			if err := runWithSimple(c, setup); err != nil {
    99  				return nil, err
   100  			}
   101  
   102  			err := invoke(c, ccs)
   103  			return c, err
   104  		}
   105  	}
   106  
   107  	var n int
   108  	f1 := func(setup initialChanges, ccs confChanges) *Changer {
   109  		c, err := wrapper(runWithSimple)(setup, ccs)
   110  		if err != nil {
   111  			t.Fatal(err)
   112  		}
   113  		if n < infoCount {
   114  			t.Log("initial setup:", Describe(setup...))
   115  			t.Log("changes:", Describe(ccs...))
   116  			t.Log(c.Tracker.Config)
   117  			t.Log(c.Tracker.Progress)
   118  		}
   119  		n++
   120  		return c
   121  	}
   122  	f2 := func(setup initialChanges, ccs confChanges) *Changer {
   123  		c, err := wrapper(runWithJoint)(setup, ccs)
   124  		if err != nil {
   125  			t.Fatal(err)
   126  		}
   127  		return c
   128  	}
   129  	err := quick.CheckEqual(f1, f2, cfg)
   130  	if err == nil {
   131  		return
   132  	}
   133  	cErr, ok := err.(*quick.CheckEqualError)
   134  	if !ok {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	t.Error("setup:", Describe(cErr.In[0].([]pb.ConfChangeSingle)...))
   139  	t.Error("ccs:", Describe(cErr.In[1].([]pb.ConfChangeSingle)...))
   140  	t.Errorf("out1: %+v\nout2: %+v", cErr.Out1, cErr.Out2)
   141  }
   142  
   143  type confChangeTyp pb.ConfChangeType
   144  
   145  func (confChangeTyp) Generate(rand *rand.Rand, _ int) reflect.Value {
   146  	return reflect.ValueOf(confChangeTyp(rand.Intn(4)))
   147  }
   148  
   149  type confChanges []pb.ConfChangeSingle
   150  
   151  func genCC(num func() int, id func() uint64, typ func() pb.ConfChangeType) []pb.ConfChangeSingle {
   152  	var ccs []pb.ConfChangeSingle
   153  	n := num()
   154  	for i := 0; i < n; i++ {
   155  		ccs = append(ccs, pb.ConfChangeSingle{Type: typ(), NodeID: id()})
   156  	}
   157  	return ccs
   158  }
   159  
   160  func (confChanges) Generate(rand *rand.Rand, _ int) reflect.Value {
   161  	num := func() int {
   162  		return 1 + rand.Intn(9)
   163  	}
   164  	id := func() uint64 {
   165  		// Note that num() >= 1, so we're never returning 1 from this method,
   166  		// meaning that we'll never touch NodeID one, which is special to avoid
   167  		// voterless configs altogether in this test.
   168  		return 1 + uint64(num())
   169  	}
   170  	typ := func() pb.ConfChangeType {
   171  		return pb.ConfChangeType(rand.Intn(len(pb.ConfChangeType_name)))
   172  	}
   173  	return reflect.ValueOf(genCC(num, id, typ))
   174  }
   175  
   176  type initialChanges []pb.ConfChangeSingle
   177  
   178  func (initialChanges) Generate(rand *rand.Rand, _ int) reflect.Value {
   179  	num := func() int {
   180  		return 1 + rand.Intn(5)
   181  	}
   182  	id := func() uint64 { return uint64(num()) }
   183  	typ := func() pb.ConfChangeType {
   184  		return pb.ConfChangeAddNode
   185  	}
   186  	// NodeID one is special - it's in the initial config and will be a voter
   187  	// always (this is to avoid uninteresting edge cases where the simple conf
   188  	// changes can't easily make progress).
   189  	ccs := append([]pb.ConfChangeSingle{{Type: pb.ConfChangeAddNode, NodeID: 1}}, genCC(num, id, typ)...)
   190  	return reflect.ValueOf(ccs)
   191  }
   192  

View as plain text