Skip to content

Commit

Permalink
Store only last 2 postgres timeline histories
Browse files Browse the repository at this point in the history
Co-authored-by: Arunvel Sriram <arunvelsriram@gmail.com>
  • Loading branch information
dineshba and arunvelsriram committed May 28, 2019
1 parent a27bcae commit dd1acb0
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 20 deletions.
52 changes: 32 additions & 20 deletions cmd/keeper/cmd/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ var CmdKeeper = &cobra.Command{
Version: cmd.Version,
}

const maxPostgresTimelinesHistory = 2

type KeeperLocalState struct {
UID string
ClusterUID string
Expand Down Expand Up @@ -676,27 +678,12 @@ func (p *PostgresKeeper) GetPGState(pctx context.Context) (*cluster.PostgresStat
pgState.TimelineID = sd.TimelineID
pgState.XLogPos = sd.XLogPos

// if timeline <= 1 then no timeline history file exists.
pgState.TimelinesHistory = cluster.PostgresTimelinesHistory{}
if pgState.TimelineID > 1 {
var tlsh []*postgresql.TimelineHistory
tlsh, err = p.pgm.GetTimelinesHistory(pgState.TimelineID)
if err != nil {
log.Errorw("error getting timeline history", zap.Error(err))
return pgState, nil
}
ctlsh := cluster.PostgresTimelinesHistory{}

for _, tlh := range tlsh {
ctlh := &cluster.PostgresTimelineHistory{
TimelineID: tlh.TimelineID,
SwitchPoint: tlh.SwitchPoint,
Reason: tlh.Reason,
}
ctlsh = append(ctlsh, ctlh)
}
pgState.TimelinesHistory = ctlsh
ctlsh, err := getTimeLinesHistory(pgState, p.pgm, maxPostgresTimelinesHistory)
if err != nil {
log.Errorw("error getting timeline history", zap.Error(err))
return pgState, nil
}
pgState.TimelinesHistory = ctlsh

ow, err := p.pgm.OlderWalFile()
if err != nil {
Expand All @@ -711,6 +698,31 @@ func (p *PostgresKeeper) GetPGState(pctx context.Context) (*cluster.PostgresStat
return pgState, nil
}

func getTimeLinesHistory(pgState *cluster.PostgresState, pgm postgresql.PGManager, maxPostgresTimelinesHistory int) (cluster.PostgresTimelinesHistory, error) {
ctlsh := cluster.PostgresTimelinesHistory{}
// if timeline <= 1 then no timeline history file exists.
if pgState.TimelineID > 1 {
var tlsh []*postgresql.TimelineHistory
tlsh, err := pgm.GetTimelinesHistory(pgState.TimelineID)
if err != nil {
log.Errorw("error getting timeline history", zap.Error(err))
return ctlsh, err
}
if len(tlsh) > maxPostgresTimelinesHistory {
tlsh = tlsh[len(tlsh)-maxPostgresTimelinesHistory:]
}
for _, tlh := range tlsh {
ctlh := &cluster.PostgresTimelineHistory{
TimelineID: tlh.TimelineID,
SwitchPoint: tlh.SwitchPoint,
Reason: tlh.Reason,
}
ctlsh = append(ctlsh, ctlh)
}
}
return ctlsh, nil
}

func (p *PostgresKeeper) getLastPGState() *cluster.PostgresState {
p.pgStateMutex.Lock()
pgState := p.lastPGState.DeepCopy()
Expand Down
158 changes: 158 additions & 0 deletions cmd/keeper/cmd/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import (
"bytes"
"errors"
"fmt"
"github.com/golang/mock/gomock"
"github.com/sorintlab/stolon/internal/mock/postgresql"
"github.com/sorintlab/stolon/internal/postgresql"
"reflect"
"testing"

Expand Down Expand Up @@ -248,3 +251,158 @@ func TestGenerateHBA(t *testing.T) {
}
}
}

func TestGetTimeLinesHistory(t *testing.T) {
t.Run("should return empty if timelineID is not greater than 1", func(t *testing.T) {
pgState := &cluster.PostgresState{
TimelineID: 1,
}

ctlsh, err := getTimeLinesHistory(pgState, nil, 3)

if err != nil {
t.Errorf("err should be nil, but got %v", err)
}
if len(ctlsh) != 0 {
t.Errorf("expecting empty ctlsh, but got %v", ctlsh)
}
})

t.Run("should return error if there is error in getting timeline history", func(t *testing.T) {
var timelineID uint64 = 2
pgState := &cluster.PostgresState{
TimelineID: timelineID,
}

ctrl := gomock.NewController(t)
defer ctrl.Finish()

pgm := mocks.NewMockPGManager(ctrl)
pgm.EXPECT().GetTimelinesHistory(timelineID).Return([]*postgresql.TimelineHistory{}, fmt.Errorf("failed to get timeline history"))
ctlsh, err := getTimeLinesHistory(pgState, pgm, 3)

if err == nil {
t.Errorf("err should be not be nil")
}
if err != nil && err.Error() != "failed to get timeline history" {
t.Errorf("err should be failed to get timeline history, but got %v", err)
}
if len(ctlsh) != 0 {
t.Errorf("expecting empty ctlsh, but got %v", ctlsh)
}
})

t.Run("should return timeline history as is if the given length is less than maxPostgresTimelinesHistory", func(t *testing.T) {
var timelineID uint64 = 2
pgState := &cluster.PostgresState{
TimelineID: timelineID,
}

ctrl := gomock.NewController(t)
defer ctrl.Finish()

pgm := mocks.NewMockPGManager(ctrl)
timelineHistories := []*postgresql.TimelineHistory{
{
TimelineID: 1,
SwitchPoint: 1,
Reason: "reason1",
},
{
TimelineID: 2,
SwitchPoint: 2,
Reason: "reason2",
},
}
pgm.EXPECT().GetTimelinesHistory(timelineID).Return(timelineHistories, nil)
ctlsh, err := getTimeLinesHistory(pgState, pgm, 3)

if err != nil {
t.Errorf("err should be not be nil")
}
if len(ctlsh) != 2 {
t.Errorf("expecting length of ctlsh to be 2, but got %d", len(ctlsh))
}
expectedTimelineHistories := cluster.PostgresTimelinesHistory{
&cluster.PostgresTimelineHistory{
TimelineID: 1,
SwitchPoint: 1,
Reason: "reason1",
},
&cluster.PostgresTimelineHistory{
TimelineID: 2,
SwitchPoint: 2,
Reason: "reason2",
},
}
fmt.Println(ctlsh, expectedTimelineHistories)
if *ctlsh[0] != *expectedTimelineHistories[0] {
t.Errorf("expected %v, but got %v ", *expectedTimelineHistories[0], *ctlsh[0])
}
if *ctlsh[1] != *expectedTimelineHistories[1] {
t.Errorf("expected %v, but got %v ", *expectedTimelineHistories[1], *ctlsh[1])
}
})

t.Run("should return timeline history with last maxPostgresTimelinesHistory elements if timeline history length is greater than maxPostgresTimelinesHistory", func(t *testing.T) {
var timelineID uint64 = 4
pgState := &cluster.PostgresState{
TimelineID: timelineID,
}

ctrl := gomock.NewController(t)
defer ctrl.Finish()

pgm := mocks.NewMockPGManager(ctrl)
timelineHistories := []*postgresql.TimelineHistory{
{
TimelineID: 1,
SwitchPoint: 1,
Reason: "reason1",
},
{
TimelineID: 2,
SwitchPoint: 2,
Reason: "reason2",
},
{
TimelineID: 3,
SwitchPoint: 3,
Reason: "reason3",
},
{
TimelineID: 4,
SwitchPoint: 4,
Reason: "reason4",
},
}
pgm.EXPECT().GetTimelinesHistory(timelineID).Return(timelineHistories, nil)
ctlsh, err := getTimeLinesHistory(pgState, pgm, 2)

if err != nil {
t.Errorf("err should be not be nil")
}
if len(ctlsh) != 2 {
t.Errorf("expecting length of ctlsh to be 2, but got %d", len(ctlsh))
}
expectedTimelineHistories := cluster.PostgresTimelinesHistory{
&cluster.PostgresTimelineHistory{
TimelineID: 3,
SwitchPoint: 3,
Reason: "reason3",
},
&cluster.PostgresTimelineHistory{
TimelineID: 4,
SwitchPoint: 4,
Reason: "reason4",
},
}
fmt.Println(ctlsh, expectedTimelineHistories)
if *ctlsh[0] != *expectedTimelineHistories[0] {
t.Errorf("expected %v, but got %v ", *expectedTimelineHistories[0], *ctlsh[0])
}
if *ctlsh[1] != *expectedTimelineHistories[1] {
t.Errorf("expected %v, but got %v ", *expectedTimelineHistories[1], *ctlsh[1])
}
})
}
47 changes: 47 additions & 0 deletions internal/mock/postgresql/postgresql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions internal/postgresql/postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import (
"go.uber.org/zap"
)

//go:generate mockgen -destination=../mock/postgresql/postgresql.go -package=mocks -source=$GOFILE

const (
postgresConf = "postgresql.conf"
postgresRecoveryConf = "recovery.conf"
Expand All @@ -53,6 +55,10 @@ var (

var log = slog.S()

type PGManager interface {
GetTimelinesHistory(timeline uint64) ([]*TimelineHistory, error)
}

type Manager struct {
pgBinPath string
dataDir string
Expand Down

0 comments on commit dd1acb0

Please sign in to comment.