Allow read replication recovery from last position

This commit is contained in:
Ben Johnson
2022-04-03 09:18:54 -06:00
parent 2c3e28c786
commit 44662022fa
8 changed files with 361 additions and 56 deletions

View File

@@ -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.