示例#1
0
func benchmarkLogEntryDecoderDecode(b *testing.B, sz int) {
	var buf bytes.Buffer
	enc := raft.NewLogEntryEncoder(&buf)
	dec := raft.NewLogEntryDecoder(&buf)

	// Encode a single record and record its size.
	enc.Encode(&raft.LogEntry{Data: make([]byte, sz)})
	b.SetBytes(int64(buf.Len()))

	// Encode all the records on the buffer first.
	buf.Reset()
	for i := 0; i < b.N; i++ {
		if err := enc.Encode(&raft.LogEntry{Data: make([]byte, sz)}); err != nil {
			b.Fatalf("encode: %s", err)
		}
	}
	b.ReportAllocs()

	// Decode from the buffer.
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var entry raft.LogEntry
		if err := dec.Decode(&entry); err != nil {
			b.Fatal(err)
		}
	}
	b.StopTimer()
	runtime.GC()
}
示例#2
0
// Ensure the decoder returns EOF when no more data is available.
func TestLogEntryDecoder_Decode_EOF(t *testing.T) {
	var e raft.LogEntry
	dec := raft.NewLogEntryDecoder(bytes.NewReader([]byte{}))
	if err := dec.Decode(&e); err != io.EOF {
		t.Fatalf("unexpected error: %s", err)
	}
}
示例#3
0
// Ensure the decoder returns an unexpected EOF when reading partial entries.
func TestLogEntryDecoder_Decode_ErrUnexpectedEOF_Type(t *testing.T) {
	for i, tt := range []struct {
		buf []byte
	}{
		{[]byte{0x10}}, // type flag only
		{[]byte{0x10, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}},             // partial header
		{[]byte{0x10, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}},          // full header, no data
		{[]byte{0x10, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5}}, // full header, partial data
	} {
		var e raft.LogEntry
		dec := raft.NewLogEntryDecoder(bytes.NewReader(tt.buf))
		if err := dec.Decode(&e); err != io.ErrUnexpectedEOF {
			t.Errorf("%d. unexpected error: %s", i, err)
		}
	}
}
示例#4
0
// Ensure that log entries can be decoded from a reader.
func TestLogEntryDecoder_Decode(t *testing.T) {
	buf := bytes.NewBuffer([]byte{0x10, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 6})

	// Create a blank entry and an expected result.
	entry := &raft.LogEntry{}

	// Decode the entry from the buffer.
	dec := raft.NewLogEntryDecoder(buf)
	if err := dec.Decode(entry); err != nil {
		t.Fatalf("unexpected error: %s", err)
	}
	if index := uint64(2); index != entry.Index {
		t.Fatalf("index: exp: %v, got: %v", index, entry.Index)
	}
	if term := uint64(3); term != entry.Term {
		t.Fatalf("term: exp: %v, got: %v", term, entry.Term)
	}
	if data := []byte{4, 5, 6}; !bytes.Equal(data, entry.Data) {
		t.Fatalf("data: exp: %x, got: %x", data, entry.Term)
	}
}
示例#5
0
// Ensure that random entries can be encoded and decoded correctly.
func TestLogEntryEncodeDecode(t *testing.T) {
	f := func(entries []raft.LogEntry) bool {
		var buf bytes.Buffer
		enc := raft.NewLogEntryEncoder(&buf)
		dec := raft.NewLogEntryDecoder(&buf)

		// Encode entries.
		for _, e := range entries {
			if e.Type == 0xFF {
				buf.WriteByte(0xFF)
				continue
			}
			if err := enc.Encode(&e); err != nil {
				t.Fatalf("encode: %s", err)
			}
		}

		// Decode entries.
		for _, e := range entries {
			var entry raft.LogEntry
			if err := dec.Decode(&entry); err != nil {
				t.Fatalf("decode: %s", err)
			} else if entry.Type == 0xFF {
				if !reflect.DeepEqual(&entry, &raft.LogEntry{Type: 0xFF}) {
					t.Fatalf("invalid snapshot entry: %#v", &entry)
				}
			} else if !reflect.DeepEqual(e, entry) {
				t.Fatalf("mismatch:\n\nexp: %#v\n\ngot: %#v\n\n", e, entry)
			}
		}

		return true
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}
示例#6
0
// Ensure a stream can be retrieved over HTTP.
func TestHTTPHandler_HandleStream(t *testing.T) {
	n := NewInitNode()
	defer n.Close()

	// Connect to stream.
	resp, err := http.Get(n.Server.URL + "/stream?id=1&term=1")
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if resp.StatusCode != http.StatusOK {
		t.Fatalf("unexpected status: %d", resp.StatusCode)
	}
	defer resp.Body.Close()

	// Ensure the stream is connected before applying a command.
	time.Sleep(10 * time.Millisecond)

	// Add an entry.
	if _, err := n.Log.Apply([]byte("xyz")); err != nil {
		t.Fatal(err)
	}

	// Move log's clock ahead & flush data.
	n.Log.Clock.Add(n.Log.HeartbeatInterval)
	n.Log.Flush()

	// Read entries from stream.
	var e raft.LogEntry
	dec := raft.NewLogEntryDecoder(resp.Body)

	// First entry should be the configuration.
	if err := dec.Decode(&e); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if e.Type != 0xFE {
		t.Fatalf("expected configuration type: %d", e.Type)
	}

	// Next entry should be the snapshot.
	if err := dec.Decode(&e); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if !reflect.DeepEqual(&e, &raft.LogEntry{Type: 0xFF, Data: nil}) {
		t.Fatalf("expected snapshot type: %d", e.Type)
	}

	// Read off the snapshot.
	var fsm FSM
	if err := fsm.Restore(resp.Body); err != nil {
		t.Fatalf("restore: %s", err)
	}

	// Read off the snapshot index.
	var index uint64
	if err := binary.Read(resp.Body, binary.BigEndian, &index); err != nil {
		t.Fatalf("read snapshot index: %s", err)
	} else if index != 1 {
		t.Fatalf("unexpected snapshot index: %d", index)
	}

	// Next entry should be the command.
	if err := dec.Decode(&e); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if !reflect.DeepEqual(&e, &raft.LogEntry{Index: 2, Term: 1, Data: []byte("xyz")}) {
		t.Fatalf("unexpected entry: %#v", &e)
	}
}