github.com/ergo-services/ergo@v1.999.224/tests/raft_data_test.go (about)

     1  //go:build !manual
     2  
     3  package tests
     4  
     5  import (
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ergo-services/ergo/etf"
    11  	"github.com/ergo-services/ergo/gen"
    12  )
    13  
    14  // F - follower
    15  // M - quorum member
    16  // L - leader
    17  // Q - quorum (all members)
    18  //
    19  // append cases:
    20  // 1. F -> M -> L -> Q ... broadcast
    21  // 2. F -> L -> Q ... broadcast
    22  // 3. M -> L - Q ... broadcast
    23  // 4. L -> Q ... broadcast
    24  //
    25  
    26  func TestRaftAppend(t *testing.T) {
    27  	fmt.Printf("\n=== Test GenRaft - append data\n")
    28  	server := &testRaft{
    29  		n:      6,
    30  		qstate: gen.RaftQuorumState5,
    31  	}
    32  	nodes, rafts, leaderSerial := startRaftCluster("append", server)
    33  
    34  	fmt.Printf("    append on a follower (send to the quorum member and forward to the leader: ")
    35  	for _, raft := range rafts {
    36  		q := raft.Quorum()
    37  		// find the follower
    38  		if q.Member == true {
    39  			continue
    40  		}
    41  
    42  		// cases 1 and 2 - send to the quorum member.
    43  		// the follower isn't able to send it to the leader (case 2)
    44  		// since it has no info about the quorum leader (quorum member forwards it
    45  		// to the leader under the hood)
    46  		ref, err := raft.Append("asdfkey", "asdfvalue")
    47  		if err != nil {
    48  			t.Fatal(err)
    49  		}
    50  		leaderSerial = checkAppend(t, server, ref, rafts, leaderSerial)
    51  		break
    52  	}
    53  	fmt.Println("OK")
    54  	fmt.Printf("    append on a quorum member (send to the leader): ")
    55  	for _, raft := range rafts {
    56  		q := raft.Quorum()
    57  		// find the quorum member
    58  		if q.Member == false {
    59  			continue
    60  		}
    61  
    62  		// case 3 - quorum member sends append to the leader
    63  		ref, err := raft.Append("asdfkey", "asdfvalue")
    64  		if err != nil {
    65  			t.Fatal(err)
    66  		}
    67  		leaderSerial = checkAppend(t, server, ref, rafts, leaderSerial)
    68  		break
    69  	}
    70  	fmt.Println("OK")
    71  	fmt.Printf("    append on a leader: ")
    72  	for _, raft := range rafts {
    73  		l := raft.Leader()
    74  		// finde the quorum leader
    75  		if l == nil || l.Leader != raft.Self() {
    76  			continue
    77  		}
    78  
    79  		// case 4 - leader makes append
    80  		ref, err := raft.Append("asdfkey", "asdfvalue")
    81  		if err != nil {
    82  			t.Fatal(err)
    83  		}
    84  		leaderSerial = checkAppend(t, server, ref, rafts, leaderSerial)
    85  		break
    86  	}
    87  	fmt.Println("OK")
    88  
    89  	for _, node := range nodes {
    90  		node.Stop()
    91  	}
    92  }
    93  
    94  // get serial case: run through the raft process and get all the data to achieve
    95  // the same serial
    96  func TestRaftGet(t *testing.T) {
    97  	fmt.Printf("\n=== Test GenRaft - get data\n")
    98  	server := &testRaft{
    99  		n:      6,
   100  		qstate: gen.RaftQuorumState5,
   101  	}
   102  	nodes, rafts, leaderSerial := startRaftCluster("get", server)
   103  	for _, raft := range rafts {
   104  		fmt.Println("    started raft process", raft.Self(), "with serial", raft.Serial())
   105  	}
   106  
   107  	for _, raft := range rafts {
   108  		_, err := raft.Get(12341234)
   109  		if err != gen.ErrRaftNoSerial {
   110  			t.Fatal("must be ErrRaftNoSerial here")
   111  		}
   112  
   113  		if raft.Serial() == leaderSerial {
   114  			fmt.Println("    raft process", raft.Self(), "has already latest serial. skip it")
   115  			continue
   116  		}
   117  
   118  		fmt.Printf("    get serials (%d...%d) on %s to reach the leader's serial: ", raft.Serial()+1, leaderSerial, raft.Self())
   119  		serial := raft.Serial()
   120  		gotFrom := []etf.Pid{}
   121  		for i := serial; i < leaderSerial; i++ {
   122  			ref, err := raft.Get(i + 1)
   123  			if err != nil {
   124  				t.Fatal(err)
   125  			}
   126  			result := waitGetRef(t, server, ref)
   127  
   128  			// compare with the original
   129  			original := data[result.key]
   130  			if original.serial != result.serial {
   131  				t.Fatal("wrong serial")
   132  			}
   133  			if original.value != result.value {
   134  				t.Fatal("wrong value")
   135  			}
   136  			// check internal data
   137  			internal := raft.State.(*raftState)
   138  			if internal.serials[result.serial] != result.key {
   139  				t.Fatal("serial mismatch the result key")
   140  			}
   141  			d, exist := internal.data[result.key]
   142  			if exist == false {
   143  				t.Fatal("internal data hasn't been updated")
   144  			}
   145  			if d.serial != result.serial {
   146  				t.Fatal("intenal data serial mismatch")
   147  			}
   148  			if d.value != result.value {
   149  				t.Fatal("intarnal data value mismatch")
   150  			}
   151  			gotFrom = append(gotFrom, result.process.Self())
   152  		}
   153  		fmt.Println("OK")
   154  		fmt.Println("         got from:", gotFrom, ")")
   155  	}
   156  
   157  	//fmt.Println("-----------")
   158  	//for _, raft := range rafts {
   159  	//	s := raft.State.(*raftState)
   160  	//	fmt.Println(raft.Self(), "INTERNAL", s)
   161  	//}
   162  
   163  	for _, node := range nodes {
   164  		node.Stop()
   165  	}
   166  }
   167  
   168  func waitGetRef(t *testing.T, server *testRaft, ref etf.Ref) raftResult {
   169  	var result raftResult
   170  	select {
   171  	case result = <-server.s:
   172  		if result.ref != ref {
   173  			t.Fatal("wrong ref")
   174  		}
   175  		return result
   176  	case <-time.After(30 * time.Second):
   177  		t.Fatal("get timeout")
   178  	}
   179  	return result
   180  }
   181  
   182  func checkAppend(t *testing.T, server *testRaft, ref etf.Ref, rafts []*gen.RaftProcess, serial uint64) uint64 {
   183  	appends := 0
   184  	for {
   185  		select {
   186  		case result := <-server.a:
   187  			if result.serial != serial+1 {
   188  				t.Fatalf("wrong serial %d (must be %d)", result.serial, serial+1)
   189  			}
   190  			appends++
   191  			//fmt.Println("got append on ", result.process.Self(), "total appends", appends)
   192  			if appends != len(rafts) {
   193  				continue
   194  			}
   195  			// check serials
   196  			for _, r := range rafts {
   197  				s := r.Serial()
   198  				if s != serial+1 {
   199  					t.Fatalf("wrong serial %d on %s", s, r.Self())
   200  				}
   201  			}
   202  			return serial + 1
   203  		case <-time.After(30 * time.Second):
   204  			t.Fatal("append timeout")
   205  
   206  		}
   207  	}
   208  
   209  }