From ed7709b8313336b716339b5f800ffea8d6f9020d Mon Sep 17 00:00:00 2001 From: Pavel Kalinnikov Date: Mon, 14 Oct 2024 14:15:35 +0100 Subject: [PATCH] raft: export LogSlice Epic: none Release note: none --- pkg/raft/bootstrap.go | 2 +- pkg/raft/log.go | 18 ++++++------- pkg/raft/log_test.go | 26 +++++++++--------- pkg/raft/log_unstable.go | 50 +++++++++++++++++------------------ pkg/raft/log_unstable_test.go | 30 ++++++++++----------- pkg/raft/raft.go | 20 +++++++------- pkg/raft/rawnode.go | 2 +- pkg/raft/storage.go | 4 +-- pkg/raft/types.go | 40 +++++++++++++--------------- pkg/raft/types_test.go | 12 ++++----- 10 files changed, 101 insertions(+), 103 deletions(-) diff --git a/pkg/raft/bootstrap.go b/pkg/raft/bootstrap.go index b9c8d41a346c..be895214437b 100644 --- a/pkg/raft/bootstrap.go +++ b/pkg/raft/bootstrap.go @@ -46,7 +46,7 @@ func (rn *RawNode) Bootstrap(peers []Peer) error { // TODO(tbg): remove StartNode and give the application the right tools to // bootstrap the initial membership in a cleaner way. rn.raft.becomeFollower(1, None) - app := logSlice{term: 1, entries: make([]pb.Entry, 0, len(peers))} + app := LogSlice{term: 1, entries: make([]pb.Entry, 0, len(peers))} for i, peer := range peers { cc := pb.ConfChange{Type: pb.ConfChangeAddNode, NodeID: peer.ID, Context: peer.Context} data, err := cc.Marshal() diff --git a/pkg/raft/log.go b/pkg/raft/log.go index 7463806cf742..a91945570e68 100644 --- a/pkg/raft/log.go +++ b/pkg/raft/log.go @@ -41,7 +41,7 @@ type LogSnapshot struct { // storage contains the stable log entries. storage LogStorage // unstable contains the unstable log entries. - unstable logSlice + unstable LogSlice // logger gives access to logging errors. logger raftlogger.Logger } @@ -155,7 +155,7 @@ func (l *raftLog) accTerm() uint64 { // the log (so this log slice is insufficient to make our log consistent with // the leader log), the slice is out of bounds (appending it would introduce a // gap), or a.term is outdated. -func (l *raftLog) maybeAppend(a logSlice) bool { +func (l *raftLog) maybeAppend(a LogSlice) bool { match, ok := l.match(a) if !ok { return false @@ -179,7 +179,7 @@ func (l *raftLog) maybeAppend(a logSlice) bool { // // Returns false if the operation can not be done: entry a.prev does not match // the lastEntryID of this log, or a.term is outdated. -func (l *raftLog) append(a logSlice) bool { +func (l *raftLog) append(a LogSlice) bool { return l.unstable.append(a) } @@ -191,8 +191,8 @@ func (l *raftLog) append(a logSlice) bool { // // All the entries up to the returned index are already present in the log, and // do not need to be rewritten. The caller can safely fast-forward the appended -// logSlice to this index. -func (l *raftLog) match(s logSlice) (uint64, bool) { +// LogSlice to this index. +func (l *raftLog) match(s LogSlice) (uint64, bool) { if !l.matchTerm(s.prev) { return 0, false } @@ -558,13 +558,13 @@ func (l LogSnapshot) LogSlice(lo, hi uint64, maxSize uint64) (LogSlice, error) { if err != nil { // The log is probably compacted at index > lo (err == ErrCompacted), or it // can be a custom storage error. - return logSlice{}, err + return LogSlice{}, err } ents, err := l.slice(lo, hi, entryEncodingSize(maxSize)) if err != nil { - return logSlice{}, err + return LogSlice{}, err } - return logSlice{ + return LogSlice{ term: l.unstable.term, prev: entryID{term: prevTerm, index: lo}, entries: ents, @@ -659,7 +659,7 @@ func (l *raftLog) snap(storage LogStorage) LogSnapshot { return LogSnapshot{ first: l.firstIndex(), storage: storage, - unstable: l.unstable.logSlice, + unstable: l.unstable.LogSlice, logger: l.logger, } } diff --git a/pkg/raft/log_test.go b/pkg/raft/log_test.go index e4ae9fb1d461..daf61ef9e92f 100644 --- a/pkg/raft/log_test.go +++ b/pkg/raft/log_test.go @@ -33,7 +33,7 @@ func TestMatch(t *testing.T) { ids[i] = entryID{term: init.termAt(uint64(i)), index: uint64(i)} } for _, tt := range []struct { - sl logSlice + sl LogSlice notOk bool want uint64 }{ @@ -42,7 +42,7 @@ func TestMatch(t *testing.T) { {sl: entryID{term: 4, index: 1}.append(4, 4), notOk: true}, {sl: entryID{term: 5, index: 2}.append(5, 6), notOk: true}, // no conflict, empty slice - {sl: logSlice{}, want: 0}, + {sl: LogSlice{}, want: 0}, // no conflict {sl: ids[0].append(1, 2, 3), want: 3}, {sl: ids[1].append(2, 3), want: 3}, @@ -75,7 +75,7 @@ func TestFindConflictByTerm(t *testing.T) { noSnap := entryID{} snap10 := entryID{term: 3, index: 10} for _, tt := range []struct { - sl logSlice + sl LogSlice index uint64 term uint64 want uint64 @@ -155,12 +155,12 @@ func TestIsUpToDate(t *testing.T) { func TestAppend(t *testing.T) { init := entryID{}.append(1, 2, 2) for _, tt := range []struct { - app logSlice - want logSlice + app LogSlice + want LogSlice notOk bool }{ // appends not at the end of the log - {app: logSlice{}, notOk: true}, + {app: LogSlice{}, notOk: true}, {app: entryID{term: 1, index: 1}.append(3), notOk: true}, {app: entryID{term: 2, index: 4}.append(3), notOk: true}, // appends at the end of the log @@ -204,7 +204,7 @@ func TestLogMaybeAppend(t *testing.T) { commit := uint64(1) for _, tt := range []struct { - app logSlice + app LogSlice want []pb.Entry notOk bool panic bool @@ -222,7 +222,7 @@ func TestLogMaybeAppend(t *testing.T) { {app: last.append(4), want: index(1).terms(1, 2, 3, 4)}, {app: last.append(4, 4), want: index(1).terms(1, 2, 3, 4, 4)}, // appends from before the end of the log - {app: logSlice{}, want: init.entries}, + {app: LogSlice{}, want: init.entries}, {app: entryID{term: 1, index: 1}.append(4), want: index(1).terms(1, 4)}, {app: entryID{term: 1, index: 1}.append(4, 4), want: index(1).terms(1, 4, 4)}, {app: entryID{term: 2, index: 2}.append(4), want: index(1).terms(1, 2, 4)}, @@ -507,7 +507,7 @@ func TestAppliedTo(t *testing.T) { // the entries correctly, before and after making them stable. func TestNextUnstableEnts(t *testing.T) { init := entryID{}.append(1, 2) - for _, tt := range []logSlice{ + for _, tt := range []LogSlice{ init.lastEntryID().append(), init.lastEntryID().append(2, 2), init.lastEntryID().append(3, 4, 5, 6), @@ -590,7 +590,7 @@ func TestStableToWithSnap(t *testing.T) { snapID := entryID{term: 2, index: 5} snap := pb.Snapshot{Metadata: pb.SnapshotMetadata{Term: snapID.term, Index: snapID.index}} for _, tt := range []struct { - sl logSlice + sl LogSlice to LogMark want uint64 // prev.index }{ @@ -953,15 +953,15 @@ func (i index) termRange(from, to uint64) []pb.Entry { return i.terms(intRange(from, to)...) } -// append generates a valid logSlice of entries appended after the given entry +// append generates a valid LogSlice of entries appended after the given entry // ID, at indices [id.index+1, id.index+len(terms)], with the given terms of // each entry. Terms must be >= id.term, and non-decreasing. -func (id entryID) append(terms ...uint64) logSlice { +func (id entryID) append(terms ...uint64) LogSlice { term := id.term if ln := len(terms); ln != 0 { term = terms[ln-1] } - ls := logSlice{ + ls := LogSlice{ term: term, prev: id, entries: index(id.index + 1).terms(terms...), diff --git a/pkg/raft/log_unstable.go b/pkg/raft/log_unstable.go index 8893b06692f2..f0ebe0c52970 100644 --- a/pkg/raft/log_unstable.go +++ b/pkg/raft/log_unstable.go @@ -26,14 +26,14 @@ import ( // "log" can be represented by a snapshot, and/or a contiguous slice of entries. // // The possible states: -// 1. Both the snapshot and the entries logSlice are empty. This means the log -// is fully in Storage. The logSlice.prev is the lastEntryID of the log. -// 2. The snapshot is empty, and the logSlice is non-empty. The state up to -// (including) logSlice.prev is in Storage, and the logSlice is pending. -// 3. The snapshot is non-empty, and the logSlice is empty. The snapshot +// 1. Both the snapshot and the entries LogSlice are empty. This means the log +// is fully in Storage. The LogSlice.prev is the lastEntryID of the log. +// 2. The snapshot is empty, and the LogSlice is non-empty. The state up to +// (including) LogSlice.prev is in Storage, and the LogSlice is pending. +// 3. The snapshot is non-empty, and the LogSlice is empty. The snapshot // overrides the entire log in Storage. -// 4. Both the snapshot and logSlice are non-empty. The snapshot immediately -// precedes the entries, i.e. logSlice.prev == snapshot.lastEntryID. This +// 4. Both the snapshot and LogSlice are non-empty. The snapshot immediately +// precedes the entries, i.e. LogSlice.prev == snapshot.lastEntryID. This // state overrides the entire log in Storage. // // The type serves two roles. First, it holds on to the latest snapshot / log @@ -50,7 +50,7 @@ import ( // no strict requirement on the order of acknowledgement delivery. // // TODO(pav-kv): describe the order requirements in more detail when accTerm -// (logSlice.term) is integrated into the protocol. +// (LogSlice.term) is integrated into the protocol. // // Note that the in-memory prefix of the log can contain entries at indices less // than Storage.LastIndex(). This means that the next write to storage might @@ -64,31 +64,31 @@ type unstable struct { // snapshot is the pending unstable snapshot, if any. // // Invariant: snapshot == nil ==> !snapshotInProgress - // Invariant: snapshot != nil ==> snapshot.lastEntryID == logSlice.prev + // Invariant: snapshot != nil ==> snapshot.lastEntryID == LogSlice.prev // // The last invariant enforces the order of handling a situation when there is // both a snapshot and entries. The snapshot write must be acknowledged first, - // before entries are acknowledged and the logSlice moves forward. + // before entries are acknowledged and the LogSlice moves forward. snapshot *pb.Snapshot - // logSlice is the suffix of the raft log that is not yet written to storage. + // LogSlice is the suffix of the raft log that is not yet written to storage. // If all the entries are written, or covered by the pending snapshot, then - // logSlice.entries is empty. + // LogSlice.entries is empty. // - // Invariant: snapshot != nil ==> logSlice.prev == snapshot.lastEntryID - // Invariant: snapshot == nil ==> logSlice.prev is in Storage - // Invariant: logSlice.lastEntryID() is the end of the log at all times + // Invariant: snapshot != nil ==> LogSlice.prev == snapshot.lastEntryID + // Invariant: snapshot == nil ==> LogSlice.prev is in Storage + // Invariant: LogSlice.lastEntryID() is the end of the log at all times // - // Invariant: logSlice.term, a.k.a. the "last accepted term", is the term of + // Invariant: LogSlice.term, a.k.a. the "last accepted term", is the term of // the leader whose append (either entries or snapshot) we accepted last. Our // state is consistent with the leader log at this term. - logSlice + LogSlice // snapshotInProgress is true if the snapshot is being written to storage. // // Invariant: snapshotInProgress ==> snapshot != nil snapshotInProgress bool - // entryInProgress is the index of the last entry in logSlice already present + // entryInProgress is the index of the last entry in LogSlice already present // in, or being written to storage. // // Invariant: prev.index <= entryInProgress <= lastIndex() @@ -103,7 +103,7 @@ type unstable struct { } func newUnstable(last entryID, logger raftlogger.Logger) unstable { - // To initialize the last accepted term (logSlice.term) correctly, we make + // To initialize the last accepted term (LogSlice.term) correctly, we make // sure its invariant is true: the log is a prefix of the term's leader's log. // This can be achieved by conservatively initializing to the term of the last // log entry. @@ -119,7 +119,7 @@ func newUnstable(last entryID, logger raftlogger.Logger) unstable { // leader Term) gives us more information about the log, and then allows // bumping its commit index sooner than when the next MsgApp arrives. return unstable{ - logSlice: logSlice{term: last.term, prev: last}, + LogSlice: LogSlice{term: last.term, prev: last}, entryInProgress: last.index, logger: logger, } @@ -190,7 +190,7 @@ func (u *unstable) stableTo(mark LogMark) { u.logger.Panicf("mark %+v acked earlier than the snapshot(in-progress=%t): %s", mark, u.snapshotInProgress, DescribeSnapshot(*u.snapshot)) } - u.logSlice = u.forward(mark.Index) + u.LogSlice = u.forward(mark.Index) // TODO(pav-kv): why can mark.index overtake u.entryInProgress? Probably bugs // in tests using the log writes incorrectly, e.g. TestLeaderStartReplication // takes nextUnstableEnts() without acceptInProgress(). @@ -246,7 +246,7 @@ func (u *unstable) restore(s snapshot) bool { term := max(u.term, s.term) u.snapshot = &s.snap - u.logSlice = logSlice{term: term, prev: s.lastEntryID()} + u.LogSlice = LogSlice{term: term, prev: s.lastEntryID()} u.snapshotInProgress = false u.entryInProgress = u.prev.index return true @@ -254,7 +254,7 @@ func (u *unstable) restore(s snapshot) bool { // append adds the given log slice to the end of the log. Returns false if this // can not be done. -func (u *unstable) append(a logSlice) bool { +func (u *unstable) append(a LogSlice) bool { if a.term < u.term { return false // append from an outdated log } else if a.prev != u.lastEntryID() { @@ -265,7 +265,7 @@ func (u *unstable) append(a logSlice) bool { return true } -func (u *unstable) truncateAndAppend(a logSlice) bool { +func (u *unstable) truncateAndAppend(a LogSlice) bool { if a.term < u.term { return false // append from an outdated log } @@ -307,7 +307,7 @@ func (u *unstable) truncateAndAppend(a logSlice) bool { // Truncate the log and append new entries. Regress the entryInProgress mark // to reflect that the truncated entries are no longer considered in progress. if a.prev.index <= u.prev.index { - u.logSlice = a // replace the entire logSlice with the latest append + u.LogSlice = a // replace the entire LogSlice with the latest append // TODO(pav-kv): clean up the logging message. It will change all datadriven // test outputs, so do it in a contained PR. u.logger.Infof("replace the unstable entries from index %d", a.prev.index+1) diff --git a/pkg/raft/log_unstable_test.go b/pkg/raft/log_unstable_test.go index 691fc6a07ccf..368633fb834c 100644 --- a/pkg/raft/log_unstable_test.go +++ b/pkg/raft/log_unstable_test.go @@ -25,10 +25,10 @@ import ( "github.com/stretchr/testify/require" ) -func newUnstableForTesting(ls logSlice, snap *pb.Snapshot) unstable { +func newUnstableForTesting(ls LogSlice, snap *pb.Snapshot) unstable { return unstable{ snapshot: snap, - logSlice: ls, + LogSlice: ls, entryInProgress: ls.prev.index, logger: raftlogger.DiscardLogger, } @@ -36,7 +36,7 @@ func newUnstableForTesting(ls logSlice, snap *pb.Snapshot) unstable { func (u *unstable) checkInvariants(t testing.TB) { t.Helper() - require.NoError(t, u.logSlice.valid()) + require.NoError(t, u.LogSlice.valid()) require.GreaterOrEqual(t, u.entryInProgress, u.prev.index) require.LessOrEqual(t, u.entryInProgress, u.lastIndex()) if u.snapshot != nil { @@ -54,7 +54,7 @@ func TestUnstableMaybeFirstIndex(t *testing.T) { prev4 := entryID{term: 1, index: 4} snap4 := &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}} for _, tt := range []struct { - ls logSlice + ls LogSlice snap *pb.Snapshot wok bool @@ -66,7 +66,7 @@ func TestUnstableMaybeFirstIndex(t *testing.T) { false, 0, }, { - logSlice{}, nil, + LogSlice{}, nil, false, 0, }, // has snapshot @@ -94,14 +94,14 @@ func TestLastIndex(t *testing.T) { prev4 := entryID{term: 1, index: 4} snap4 := &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}} for _, tt := range []struct { - ls logSlice + ls LogSlice snap *pb.Snapshot windex uint64 }{ {prev4.append(1), nil, 5}, // last in entries {prev4.append(1), snap4, 5}, {prev4.append(), snap4, 4}, // last in snapshot - {logSlice{}, nil, 0}, // empty unstable + {LogSlice{}, nil, 0}, // empty unstable } { t.Run("", func(t *testing.T) { u := newUnstableForTesting(tt.ls, tt.snap) @@ -136,7 +136,7 @@ func TestUnstableRestore(t *testing.T) { func TestUnstableNextEntries(t *testing.T) { prev4 := entryID{term: 1, index: 4} for _, tt := range []struct { - ls logSlice + ls LogSlice entryInProgress uint64 wentries []pb.Entry @@ -205,7 +205,7 @@ func TestUnstableAcceptInProgress(t *testing.T) { prev4 := entryID{term: 1, index: 4} snap4 := &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}} for _, tt := range []struct { - ls logSlice + ls LogSlice snap *pb.Snapshot entryInProgress uint64 snapshotInProgress bool @@ -311,7 +311,7 @@ func TestUnstableStableTo(t *testing.T) { prev4 := entryID{term: 1, index: 4} snap4 := &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}} for _, tt := range []struct { - ls logSlice + ls LogSlice entryInProgress uint64 snap *pb.Snapshot index, term uint64 @@ -321,7 +321,7 @@ func TestUnstableStableTo(t *testing.T) { wlen int }{ { - logSlice{}, 0, nil, + LogSlice{}, 0, nil, 5, 1, 0, 0, 0, }, @@ -410,12 +410,12 @@ func TestUnstableStableTo(t *testing.T) { func TestUnstableTruncateAndAppend(t *testing.T) { prev4 := entryID{term: 1, index: 4} for _, tt := range []struct { - ls logSlice + ls LogSlice entryInProgress uint64 snap *pb.Snapshot - app logSlice + app LogSlice - want logSlice + want LogSlice wentryInProgress uint64 }{ // append to the end @@ -484,7 +484,7 @@ func TestUnstableTruncateAndAppend(t *testing.T) { u.truncateAndAppend(tt.app) u.checkInvariants(t) - require.Equal(t, tt.want, u.logSlice) + require.Equal(t, tt.want, u.LogSlice) require.Equal(t, tt.wentryInProgress, u.entryInProgress) }) } diff --git a/pkg/raft/raft.go b/pkg/raft/raft.go index ead284009c09..a312c88fa593 100644 --- a/pkg/raft/raft.go +++ b/pkg/raft/raft.go @@ -611,7 +611,7 @@ func (r *raft) send(m pb.Message) { // prepareMsgApp constructs a MsgApp message for being sent to the given peer, // and hands it over to the caller. Updates the replication flow control state // to account for the fact that the message is about to be sent. -func (r *raft) prepareMsgApp(to pb.PeerID, pr *tracker.Progress, ls logSlice) pb.Message { +func (r *raft) prepareMsgApp(to pb.PeerID, pr *tracker.Progress, ls LogSlice) pb.Message { commit := r.raftLog.committed // Update the progress accordingly to the message being sent. pr.SentEntries(len(ls.entries), uint64(payloadsSize(ls.entries))) @@ -635,7 +635,7 @@ func (r *raft) prepareMsgApp(to pb.PeerID, pr *tracker.Progress, ls logSlice) pb // // Returns false if the current state of the node does not permit this MsgApp // send, e.g. the log slice is misaligned with the replication flow status. -func (r *raft) maybePrepareMsgApp(to pb.PeerID, ls logSlice) (pb.Message, bool) { +func (r *raft) maybePrepareMsgApp(to pb.PeerID, ls LogSlice) (pb.Message, bool) { if r.state != pb.StateLeader || r.Term != ls.term { return pb.Message{}, false } @@ -684,7 +684,7 @@ func (r *raft) maybeSendAppend(to pb.PeerID) bool { } } - r.send(r.prepareMsgApp(to, pr, logSlice{ + r.send(r.prepareMsgApp(to, pr, LogSlice{ term: r.Term, prev: entryID{index: prevIndex, term: prevTerm}, entries: entries, @@ -708,7 +708,7 @@ func (r *raft) sendPing(to pb.PeerID) bool { return false } // NB: this sets MsgAppProbesPaused to true again. - r.send(r.prepareMsgApp(to, pr, logSlice{ + r.send(r.prepareMsgApp(to, pr, LogSlice{ term: r.Term, prev: entryID{index: prevIndex, term: prevTerm}, })) @@ -1019,7 +1019,7 @@ func (r *raft) appendEntry(es ...pb.Entry) (accepted bool) { // Drop the proposal. return false } - app := logSlice{term: r.Term, prev: last, entries: es} + app := LogSlice{term: r.Term, prev: last, entries: es} if err := app.valid(); err != nil { r.logger.Panicf("%x leader could not append to its log: %v", r.id, err) } else if !r.raftLog.append(app) { @@ -2081,10 +2081,10 @@ func stepFollower(r *raft, m pb.Message) error { return nil } -// logSliceFromMsgApp extracts the appended logSlice from a MsgApp message. -func logSliceFromMsgApp(m *pb.Message) logSlice { - // TODO(pav-kv): consider also validating the logSlice here. - return logSlice{ +// logSliceFromMsgApp extracts the appended LogSlice from a MsgApp message. +func logSliceFromMsgApp(m *pb.Message) LogSlice { + // TODO(pav-kv): consider also validating the LogSlice here. + return LogSlice{ term: m.Term, prev: entryID{term: m.LogTerm, index: m.Index}, entries: m.Entries, @@ -2094,7 +2094,7 @@ func logSliceFromMsgApp(m *pb.Message) logSlice { func (r *raft) handleAppendEntries(m pb.Message) { r.checkMatch(m.Match) - // TODO(pav-kv): construct logSlice up the stack next to receiving the + // TODO(pav-kv): construct LogSlice up the stack next to receiving the // message, and validate it before taking any action (e.g. bumping term). a := logSliceFromMsgApp(&m) if err := a.valid(); err != nil { diff --git a/pkg/raft/rawnode.go b/pkg/raft/rawnode.go index bd52ddf1af81..0329effba2c3 100644 --- a/pkg/raft/rawnode.go +++ b/pkg/raft/rawnode.go @@ -206,7 +206,7 @@ func (rn *RawNode) LogSnapshot() LogSnapshot { // - the first slice index matches the Next index to send to this peer // // Returns false if the message can not be sent. -func (rn *RawNode) SendMsgApp(to pb.PeerID, slice logSlice) (pb.Message, bool) { +func (rn *RawNode) SendMsgApp(to pb.PeerID, slice LogSlice) (pb.Message, bool) { return rn.raft.maybePrepareMsgApp(to, slice) } diff --git a/pkg/raft/storage.go b/pkg/raft/storage.go index 2f55a3745973..9848818def39 100644 --- a/pkg/raft/storage.go +++ b/pkg/raft/storage.go @@ -82,8 +82,8 @@ type LogStorage interface { // FirstIndex still returns the snapshot index + 1, yet the first log entry at // this index is not available. // - // TODO(pav-kv): replace this with a Prev() method equivalent to logSlice's - // prev field. The log storage is just a storage-backed logSlice. + // TODO(pav-kv): replace this with a Prev() method equivalent to LogSlice's + // prev field. The log storage is just a storage-backed LogSlice. FirstIndex() uint64 // LogSnapshot returns an immutable point-in-time log storage snapshot. diff --git a/pkg/raft/types.go b/pkg/raft/types.go index cc5fbf4993f8..2d55a360d9e2 100644 --- a/pkg/raft/types.go +++ b/pkg/raft/types.go @@ -61,21 +61,19 @@ func (l LogMark) After(other LogMark) bool { return l.Term > other.Term || l.Term == other.Term && l.Index > other.Index } -type LogSlice = logSlice // TODO(pav-kv): export logSlice properly - -// logSlice describes a correct slice of a raft log. +// LogSlice describes a correct slice of a raft log. // // Every log slice is considered in a context of a specific leader term. This // term does not necessarily match entryID.term of the entries, since a leader // log contains both entries from its own term, and some earlier terms. // -// Two slices with a matching logSlice.term are guaranteed to be consistent, +// Two slices with a matching LogSlice.term are guaranteed to be consistent, // i.e. they never contain two different entries at the same index. The reverse -// is not true: two slices with different logSlice.term may contain both +// is not true: two slices with different LogSlice.term may contain both // matching and mismatching entries. Specifically, logs at two different leader // terms share a common prefix, after which they *permanently* diverge. // -// A well-formed logSlice conforms to raft safety properties. It provides the +// A well-formed LogSlice conforms to raft safety properties. It provides the // following guarantees: // // 1. entries[i].Index == prev.index + 1 + i, @@ -88,11 +86,11 @@ type LogSlice = logSlice // TODO(pav-kv): export logSlice properly // leader log at a specific term never has entries from higher terms. // // Users of this struct can assume the invariants hold true. Exception is the -// "gateway" code that initially constructs logSlice, such as when its content +// "gateway" code that initially constructs LogSlice, such as when its content // is sourced from a message that was received via transport, or from Storage, // or in a test code that manually hard-codes this struct. In these cases, the // invariants should be validated using the valid() method. -type logSlice struct { +type LogSlice struct { // term is the leader term containing the given entries in its log. term uint64 // prev is the ID of the entry immediately preceding the entries. @@ -103,51 +101,51 @@ type logSlice struct { // lastIndex returns the index of the last entry in this log slice. Returns // prev.index if there are no entries. -func (s logSlice) lastIndex() uint64 { +func (s LogSlice) lastIndex() uint64 { return s.prev.index + uint64(len(s.entries)) } // lastEntryID returns the ID of the last entry in this log slice, or prev if // there are no entries. -func (s logSlice) lastEntryID() entryID { +func (s LogSlice) lastEntryID() entryID { if ln := len(s.entries); ln != 0 { return pbEntryID(&s.entries[ln-1]) } return s.prev } -// mark returns the LogMark identifying the end of this logSlice. -func (s logSlice) mark() LogMark { +// mark returns the LogMark identifying the end of this LogSlice. +func (s LogSlice) mark() LogMark { return LogMark{Term: s.term, Index: s.lastIndex()} } // termAt returns the term of the entry at the given index. // Requires: prev.index <= index <= lastIndex(). -func (s logSlice) termAt(index uint64) uint64 { +func (s LogSlice) termAt(index uint64) uint64 { if index == s.prev.index { return s.prev.term } return s.entries[index-s.prev.index-1].Term } -// forward returns a logSlice with prev forwarded to the given index. +// forward returns a LogSlice with prev forwarded to the given index. // Requires: prev.index <= index <= lastIndex(). -func (s logSlice) forward(index uint64) logSlice { - return logSlice{ +func (s LogSlice) forward(index uint64) LogSlice { + return LogSlice{ term: s.term, prev: entryID{term: s.termAt(index), index: index}, entries: s.entries[index-s.prev.index:], } } -// sub returns the entries of this logSlice with indices in (after, to]. -func (s logSlice) sub(after, to uint64) []pb.Entry { +// sub returns the entries of this LogSlice with indices in (after, to]. +func (s LogSlice) sub(after, to uint64) []pb.Entry { return s.entries[after-s.prev.index : to-s.prev.index] } -// valid returns nil iff the logSlice is a well-formed log slice. See logSlice +// valid returns nil iff the LogSlice is a well-formed log slice. See LogSlice // comment for details on what constitutes a valid raft log slice. -func (s logSlice) valid() error { +func (s LogSlice) valid() error { prev := s.prev for i := range s.entries { id := pbEntryID(&s.entries[i]) @@ -165,7 +163,7 @@ func (s logSlice) valid() error { // snapshot is a state machine snapshot tied to the term of the leader who // observed this committed state. // -// Semantically, from the log perspective, this type is equivalent to a logSlice +// Semantically, from the log perspective, this type is equivalent to a LogSlice // from 0 to lastEntryID(), plus a commit LogMark. All leader logs at terms >= // snapshot.term contain all entries up to the lastEntryID(). At earlier terms, // logs may or may not be consistent with this snapshot, depending on whether diff --git a/pkg/raft/types_test.go b/pkg/raft/types_test.go index 920c0d070c9a..af864bbf422e 100644 --- a/pkg/raft/types_test.go +++ b/pkg/raft/types_test.go @@ -84,7 +84,7 @@ func TestLogSlice(t *testing.T) { {term: 10, prev: id(12, 2), entries: []pb.Entry{e(13, 2), e(14, 3)}, last: id(14, 3)}, } { t.Run("", func(t *testing.T) { - s := logSlice{term: tt.term, prev: tt.prev, entries: tt.entries} + s := LogSlice{term: tt.term, prev: tt.prev, entries: tt.entries} require.Equal(t, tt.notOk, s.valid() != nil) if tt.notOk { return @@ -106,20 +106,20 @@ func TestLogSliceForward(t *testing.T) { id := func(index, term uint64) entryID { return entryID{term: term, index: index} } - ls := func(prev entryID, terms ...uint64) logSlice { + ls := func(prev entryID, terms ...uint64) LogSlice { empty := make([]pb.Entry, 0) // hack to canonicalize empty slices - return logSlice{ + return LogSlice{ term: 8, prev: prev, entries: append(empty, index(prev.index+1).terms(terms...)...), } } for _, tt := range []struct { - ls logSlice + ls LogSlice to uint64 - want logSlice + want LogSlice }{ - {ls: logSlice{}, to: 0, want: logSlice{}}, + {ls: LogSlice{}, to: 0, want: LogSlice{}}, {ls: ls(id(5, 1)), to: 5, want: ls(id(5, 1))}, {ls: ls(id(10, 3), 3, 4, 5), to: 10, want: ls(id(10, 3), 3, 4, 5)}, {ls: ls(id(10, 3), 3, 4, 5), to: 11, want: ls(id(11, 3), 4, 5)},