2014-11-03 23:04:24 +03:00
|
|
|
/*
|
|
|
|
Copyright 2014 CoreOS, Inc.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package raft
|
|
|
|
|
|
|
|
import (
|
2014-11-13 02:21:30 +03:00
|
|
|
"errors"
|
2014-11-03 23:04:24 +03:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
pb "github.com/coreos/etcd/raft/raftpb"
|
|
|
|
)
|
|
|
|
|
2014-11-18 00:37:46 +03:00
|
|
|
// ErrSnapshotRequired is returned by Storage.Entries when a requested
|
|
|
|
// index is unavailable because it predates the last snapshot.
|
|
|
|
var ErrSnapshotRequired = errors.New("snapshot required; requested index is too old")
|
2014-11-13 02:21:30 +03:00
|
|
|
|
2014-11-03 23:04:24 +03:00
|
|
|
// Storage is an interface that may be implemented by the application
|
2014-11-13 00:23:42 +03:00
|
|
|
// to retrieve log entries from storage.
|
2014-11-03 23:04:24 +03:00
|
|
|
//
|
|
|
|
// If any Storage method returns an error, the raft instance will
|
|
|
|
// become inoperable and refuse to participate in elections; the
|
|
|
|
// application is responsible for cleanup and recovery in this case.
|
|
|
|
type Storage interface {
|
2014-11-14 21:53:42 +03:00
|
|
|
// Entries returns a slice of log entries in the range [lo,hi).
|
|
|
|
Entries(lo, hi uint64) ([]pb.Entry, error)
|
2014-11-18 00:37:46 +03:00
|
|
|
// Term returns the term of entry i, which must be in the range
|
|
|
|
// [FirstIndex()-1, LastIndex()]. The term of the entry before
|
|
|
|
// FirstIndex is retained for matching purposes even though the
|
|
|
|
// rest of that entry may not be available.
|
|
|
|
Term(i uint64) (uint64, error)
|
|
|
|
// LastIndex returns the index of the last entry in the log.
|
2014-11-14 21:53:42 +03:00
|
|
|
LastIndex() (uint64, error)
|
2014-11-18 00:37:46 +03:00
|
|
|
// FirstIndex returns the index of the first log entry that is
|
|
|
|
// available via Entries (older entries have been incorporated
|
2014-11-03 23:04:24 +03:00
|
|
|
// into the latest Snapshot).
|
2014-11-14 21:53:42 +03:00
|
|
|
FirstIndex() (uint64, error)
|
|
|
|
// Compact discards all log entries prior to i.
|
|
|
|
// TODO(bdarnell): Create a snapshot which can be used to
|
|
|
|
// reconstruct the state at that point.
|
2014-11-03 23:04:24 +03:00
|
|
|
Compact(i uint64) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// MemoryStorage implements the Storage interface backed by an
|
|
|
|
// in-memory array.
|
|
|
|
type MemoryStorage struct {
|
|
|
|
// Protects access to all fields. Most methods of MemoryStorage are
|
|
|
|
// run on the raft goroutine, but Append() is run on an application
|
|
|
|
// goroutine.
|
|
|
|
sync.Mutex
|
|
|
|
|
|
|
|
ents []pb.Entry
|
|
|
|
// offset is the position of the last compaction.
|
|
|
|
// ents[i] has raft log position i+offset.
|
|
|
|
offset uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMemoryStorage creates an empty MemoryStorage.
|
|
|
|
func NewMemoryStorage() *MemoryStorage {
|
2014-11-18 00:37:46 +03:00
|
|
|
return &MemoryStorage{
|
|
|
|
// When starting from scratch populate the list with a dummy entry at term zero.
|
|
|
|
ents: make([]pb.Entry, 1),
|
|
|
|
}
|
2014-11-03 23:04:24 +03:00
|
|
|
}
|
|
|
|
|
2014-11-14 21:53:42 +03:00
|
|
|
// Entries implements the Storage interface.
|
|
|
|
func (ms *MemoryStorage) Entries(lo, hi uint64) ([]pb.Entry, error) {
|
2014-11-03 23:04:24 +03:00
|
|
|
ms.Lock()
|
|
|
|
defer ms.Unlock()
|
2014-11-18 00:37:46 +03:00
|
|
|
if lo <= ms.offset {
|
|
|
|
return nil, ErrSnapshotRequired
|
|
|
|
}
|
2014-11-03 23:04:24 +03:00
|
|
|
return ms.ents[lo-ms.offset : hi-ms.offset], nil
|
|
|
|
}
|
|
|
|
|
2014-11-18 00:37:46 +03:00
|
|
|
// Term implements the Storage interface.
|
|
|
|
func (ms *MemoryStorage) Term(i uint64) (uint64, error) {
|
|
|
|
ms.Lock()
|
|
|
|
defer ms.Unlock()
|
|
|
|
if i < ms.offset || i > ms.offset+uint64(len(ms.ents)) {
|
|
|
|
return 0, ErrSnapshotRequired
|
|
|
|
}
|
|
|
|
return ms.ents[i-ms.offset].Term, nil
|
|
|
|
}
|
|
|
|
|
2014-11-14 21:53:42 +03:00
|
|
|
// LastIndex implements the Storage interface.
|
|
|
|
func (ms *MemoryStorage) LastIndex() (uint64, error) {
|
2014-11-03 23:04:24 +03:00
|
|
|
ms.Lock()
|
|
|
|
defer ms.Unlock()
|
|
|
|
return ms.offset + uint64(len(ms.ents)) - 1, nil
|
|
|
|
}
|
|
|
|
|
2014-11-14 21:53:42 +03:00
|
|
|
// FirstIndex implements the Storage interface.
|
|
|
|
func (ms *MemoryStorage) FirstIndex() (uint64, error) {
|
2014-11-03 23:04:24 +03:00
|
|
|
ms.Lock()
|
|
|
|
defer ms.Unlock()
|
2014-11-18 00:37:46 +03:00
|
|
|
return ms.offset + 1, nil
|
2014-11-03 23:04:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compact implements the Storage interface.
|
|
|
|
func (ms *MemoryStorage) Compact(i uint64) error {
|
|
|
|
ms.Lock()
|
|
|
|
defer ms.Unlock()
|
|
|
|
i -= ms.offset
|
|
|
|
ents := make([]pb.Entry, 1, 1+uint64(len(ms.ents))-i)
|
|
|
|
ents[0].Term = ms.ents[i].Term
|
|
|
|
ents = append(ents, ms.ents[i+1:]...)
|
|
|
|
ms.ents = ents
|
|
|
|
ms.offset += i
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append the new entries to storage.
|
|
|
|
func (ms *MemoryStorage) Append(entries []pb.Entry) {
|
|
|
|
ms.Lock()
|
|
|
|
defer ms.Unlock()
|
|
|
|
ms.ents = append(ms.ents, entries...)
|
|
|
|
}
|