Allow read replication recovery from last position
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -191,6 +192,49 @@ func (itr *WALSegmentInfoSliceIterator) WALSegment() WALSegmentInfo {
|
||||
return itr.a[0]
|
||||
}
|
||||
|
||||
type BufferedWALSegmentIterator struct {
|
||||
itr WALSegmentIterator
|
||||
buffered bool
|
||||
}
|
||||
|
||||
// NewBufferedWALSegmentIterator returns a new instance of BufferedWALSegmentIterator.
|
||||
func NewBufferedWALSegmentIterator(itr WALSegmentIterator) *BufferedWALSegmentIterator {
|
||||
return &BufferedWALSegmentIterator{itr: itr}
|
||||
}
|
||||
|
||||
// Close closes the underlying iterator.
|
||||
func (itr *BufferedWALSegmentIterator) Close() error {
|
||||
return itr.itr.Close()
|
||||
}
|
||||
|
||||
// Peek returns the next segment without moving the iterator forward.
|
||||
func (itr *BufferedWALSegmentIterator) Peek() (info WALSegmentInfo, ok bool) {
|
||||
if !itr.Next() {
|
||||
return WALSegmentInfo{}, false
|
||||
}
|
||||
itr.buffered = true
|
||||
return itr.itr.WALSegment(), true
|
||||
}
|
||||
|
||||
// Next returns the next segment. If buffer is full, this call is a no-op.
|
||||
func (itr *BufferedWALSegmentIterator) Next() bool {
|
||||
if itr.buffered {
|
||||
itr.buffered = false
|
||||
return true
|
||||
}
|
||||
return itr.itr.Next()
|
||||
}
|
||||
|
||||
// Returns an error that occurred during iteration.
|
||||
func (itr *BufferedWALSegmentIterator) Err() error {
|
||||
return itr.itr.Err()
|
||||
}
|
||||
|
||||
// Returns metadata for the currently positioned WAL segment file.
|
||||
func (itr *BufferedWALSegmentIterator) WALSegment() WALSegmentInfo {
|
||||
return itr.itr.WALSegment()
|
||||
}
|
||||
|
||||
// SnapshotInfo represents file information about a snapshot.
|
||||
type SnapshotInfo struct {
|
||||
Generation string
|
||||
@@ -302,6 +346,32 @@ type Pos struct {
|
||||
Offset int64 // offset within wal file
|
||||
}
|
||||
|
||||
// ParsePos parses a position generated by Pos.String().
|
||||
func ParsePos(s string) (Pos, error) {
|
||||
a := posRegex.FindStringSubmatch(s)
|
||||
if a == nil {
|
||||
return Pos{}, fmt.Errorf("invalid pos: %q", s)
|
||||
}
|
||||
|
||||
index, err := ParseIndex(a[2])
|
||||
if err != nil {
|
||||
return Pos{}, err
|
||||
}
|
||||
|
||||
offset, err := ParseOffset(a[3])
|
||||
if err != nil {
|
||||
return Pos{}, err
|
||||
}
|
||||
|
||||
return Pos{
|
||||
Generation: a[1],
|
||||
Index: index,
|
||||
Offset: offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var posRegex = regexp.MustCompile(`^(\w+)/(\w+):(\w+)$`)
|
||||
|
||||
// String returns a string representation.
|
||||
func (p Pos) String() string {
|
||||
if p.IsZero() {
|
||||
@@ -524,7 +594,9 @@ func (hdr *StreamRecordHeader) UnmarshalBinary(data []byte) error {
|
||||
|
||||
// StreamClient represents a client for streaming changes to a replica DB.
|
||||
type StreamClient interface {
|
||||
Stream(ctx context.Context) (StreamReader, error)
|
||||
// Stream returns a reader which contains and optional snapshot followed
|
||||
// by a series of WAL segments. This stream begins from the given position.
|
||||
Stream(ctx context.Context, pos Pos) (StreamReader, error)
|
||||
}
|
||||
|
||||
// StreamReader represents a reader that streams snapshot and WAL records.
|
||||
|
||||
Reference in New Issue
Block a user