123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- package raft
- import (
- "io/ioutil"
- "os"
- "reflect"
- "testing"
- )
- //------------------------------------------------------------------------------
- //
- // Tests
- //
- //------------------------------------------------------------------------------
- //--------------------------------------
- // Append
- //--------------------------------------
- // Ensure that we can append to a new log.
- func TestLogNewLog(t *testing.T) {
- path := getLogPath()
- log := newLog()
- log.ApplyFunc = func(e *LogEntry, c Command) (interface{}, error) {
- return nil, nil
- }
- if err := log.open(path); err != nil {
- t.Fatalf("Unable to open log: %v", err)
- }
- defer log.close()
- defer os.Remove(path)
- e, _ := newLogEntry(log, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
- if err := log.appendEntry(e); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- e, _ = newLogEntry(log, nil, 2, 1, &testCommand2{X: 100})
- if err := log.appendEntry(e); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- e, _ = newLogEntry(log, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
- if err := log.appendEntry(e); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- // Partial commit.
- if err := log.setCommitIndex(2); err != nil {
- t.Fatalf("Unable to partially commit: %v", err)
- }
- if index, term := log.commitInfo(); index != 2 || term != 1 {
- t.Fatalf("Invalid commit info [IDX=%v, TERM=%v]", index, term)
- }
- // Full commit.
- if err := log.setCommitIndex(3); err != nil {
- t.Fatalf("Unable to commit: %v", err)
- }
- if index, term := log.commitInfo(); index != 3 || term != 2 {
- t.Fatalf("Invalid commit info [IDX=%v, TERM=%v]", index, term)
- }
- }
- // Ensure that we can decode and encode to an existing log.
- func TestLogExistingLog(t *testing.T) {
- tmpLog := newLog()
- e0, _ := newLogEntry(tmpLog, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
- e1, _ := newLogEntry(tmpLog, nil, 2, 1, &testCommand2{X: 100})
- e2, _ := newLogEntry(tmpLog, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
- log, path := setupLog([]*LogEntry{e0, e1, e2})
- defer log.close()
- defer os.Remove(path)
- // Validate existing log entries.
- if len(log.entries) != 3 {
- t.Fatalf("Expected 3 entries, got %d", len(log.entries))
- }
- if log.entries[0].Index() != 1 || log.entries[0].Term() != 1 {
- t.Fatalf("Unexpected entry[0]: %v", log.entries[0])
- }
- if log.entries[1].Index() != 2 || log.entries[1].Term() != 1 {
- t.Fatalf("Unexpected entry[1]: %v", log.entries[1])
- }
- if log.entries[2].Index() != 3 || log.entries[2].Term() != 2 {
- t.Fatalf("Unexpected entry[2]: %v", log.entries[2])
- }
- }
- // Ensure that we can check the contents of the log by index/term.
- func TestLogContainsEntries(t *testing.T) {
- tmpLog := newLog()
- e0, _ := newLogEntry(tmpLog, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
- e1, _ := newLogEntry(tmpLog, nil, 2, 1, &testCommand2{X: 100})
- e2, _ := newLogEntry(tmpLog, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
- log, path := setupLog([]*LogEntry{e0, e1, e2})
- defer log.close()
- defer os.Remove(path)
- if log.containsEntry(0, 0) {
- t.Fatalf("Zero-index entry should not exist in log.")
- }
- if log.containsEntry(1, 0) {
- t.Fatalf("Entry with mismatched term should not exist")
- }
- if log.containsEntry(4, 0) {
- t.Fatalf("Out-of-range entry should not exist")
- }
- if !log.containsEntry(2, 1) {
- t.Fatalf("Entry 2/1 should exist")
- }
- if !log.containsEntry(3, 2) {
- t.Fatalf("Entry 2/1 should exist")
- }
- }
- // Ensure that we can recover from an incomplete/corrupt log and continue logging.
- func TestLogRecovery(t *testing.T) {
- tmpLog := newLog()
- e0, _ := newLogEntry(tmpLog, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
- e1, _ := newLogEntry(tmpLog, nil, 2, 1, &testCommand2{X: 100})
- f, _ := ioutil.TempFile("", "raft-log-")
- e0.Encode(f)
- e1.Encode(f)
- f.WriteString("CORRUPT!")
- f.Close()
- log := newLog()
- log.ApplyFunc = func(e *LogEntry, c Command) (interface{}, error) {
- return nil, nil
- }
- if err := log.open(f.Name()); err != nil {
- t.Fatalf("Unable to open log: %v", err)
- }
- defer log.close()
- defer os.Remove(f.Name())
- e, _ := newLogEntry(log, nil, 3, 2, &testCommand1{Val: "bat", I: -5})
- if err := log.appendEntry(e); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- // Validate existing log entries.
- if len(log.entries) != 3 {
- t.Fatalf("Expected 3 entries, got %d", len(log.entries))
- }
- if log.entries[0].Index() != 1 || log.entries[0].Term() != 1 {
- t.Fatalf("Unexpected entry[0]: %v", log.entries[0])
- }
- if log.entries[1].Index() != 2 || log.entries[1].Term() != 1 {
- t.Fatalf("Unexpected entry[1]: %v", log.entries[1])
- }
- if log.entries[2].Index() != 3 || log.entries[2].Term() != 2 {
- t.Fatalf("Unexpected entry[2]: %v", log.entries[2])
- }
- }
- //--------------------------------------
- // Append
- //--------------------------------------
- // Ensure that we can truncate uncommitted entries in the log.
- func TestLogTruncate(t *testing.T) {
- log, path := setupLog(nil)
- if err := log.open(path); err != nil {
- t.Fatalf("Unable to open log: %v", err)
- }
- defer os.Remove(path)
- entry1, _ := newLogEntry(log, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
- if err := log.appendEntry(entry1); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- entry2, _ := newLogEntry(log, nil, 2, 1, &testCommand2{X: 100})
- if err := log.appendEntry(entry2); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- entry3, _ := newLogEntry(log, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
- if err := log.appendEntry(entry3); err != nil {
- t.Fatalf("Unable to append: %v", err)
- }
- if err := log.setCommitIndex(2); err != nil {
- t.Fatalf("Unable to partially commit: %v", err)
- }
- // Truncate committed entry.
- if err := log.truncate(1, 1); err == nil || err.Error() != "raft.Log: Index is already committed (2): (IDX=1, TERM=1)" {
- t.Fatalf("Truncating committed entries shouldn't work: %v", err)
- }
- // Truncate past end of log.
- if err := log.truncate(4, 2); err == nil || err.Error() != "raft.Log: Entry index does not exist (MAX=3): (IDX=4, TERM=2)" {
- t.Fatalf("Truncating past end-of-log shouldn't work: %v", err)
- }
- // Truncate entry with mismatched term.
- if err := log.truncate(2, 2); err == nil || err.Error() != "raft.Log: Entry at index does not have matching term (1): (IDX=2, TERM=2)" {
- t.Fatalf("Truncating mismatched entries shouldn't work: %v", err)
- }
- // Truncate end of log.
- if err := log.truncate(3, 2); !(err == nil && reflect.DeepEqual(log.entries, []*LogEntry{entry1, entry2, entry3})) {
- t.Fatalf("Truncating end of log should work: %v\n\nEntries:\nActual: %v\nExpected: %v", err, log.entries, []*LogEntry{entry1, entry2, entry3})
- }
- // Truncate at last commit.
- if err := log.truncate(2, 1); !(err == nil && reflect.DeepEqual(log.entries, []*LogEntry{entry1, entry2})) {
- t.Fatalf("Truncating at last commit should work: %v\n\nEntries:\nActual: %v\nExpected: %v", err, log.entries, []*LogEntry{entry1, entry2})
- }
- // Append after truncate
- if err := log.appendEntry(entry3); err != nil {
- t.Fatalf("Unable to append after truncate: %v", err)
- }
- log.close()
- // Recovery the truncated log
- log = newLog()
- if err := log.open(path); err != nil {
- t.Fatalf("Unable to open log: %v", err)
- }
- // Validate existing log entries.
- if len(log.entries) != 3 {
- t.Fatalf("Expected 3 entries, got %d", len(log.entries))
- }
- if log.entries[0].Index() != 1 || log.entries[0].Term() != 1 {
- t.Fatalf("Unexpected entry[0]: %v", log.entries[0])
- }
- if log.entries[1].Index() != 2 || log.entries[1].Term() != 1 {
- t.Fatalf("Unexpected entry[1]: %v", log.entries[1])
- }
- if log.entries[2].Index() != 3 || log.entries[2].Term() != 2 {
- t.Fatalf("Unexpected entry[2]: %v", log.entries[2])
- }
- }
|