compactor: clean up

Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
This commit is contained in:
Gyuho Lee 2018-03-21 17:04:48 -07:00
parent f7714e269e
commit 7ce69b256a
5 changed files with 58 additions and 56 deletions

View File

@ -29,8 +29,6 @@ var (
) )
const ( const (
checkCompactionInterval = 5 * time.Minute
ModePeriodic = "periodic" ModePeriodic = "periodic"
ModeRevision = "revision" ModeRevision = "revision"
) )

View File

@ -46,30 +46,35 @@ type Periodic struct {
// NewPeriodic creates a new instance of Periodic compactor that purges // NewPeriodic creates a new instance of Periodic compactor that purges
// the log older than h Duration. // the log older than h Duration.
func NewPeriodic(h time.Duration, rg RevGetter, c Compactable) *Periodic { func NewPeriodic(h time.Duration, rg RevGetter, c Compactable) *Periodic {
return &Periodic{ return newPeriodic(clockwork.NewRealClock(), h, rg, c)
clock: clockwork.NewRealClock(), }
func newPeriodic(clock clockwork.Clock, h time.Duration, rg RevGetter, c Compactable) *Periodic {
t := &Periodic{
clock: clock,
period: h, period: h,
rg: rg, rg: rg,
c: c, c: c,
revs: make([]int64, 0),
} }
t.ctx, t.cancel = context.WithCancel(context.Background())
return t
} }
// periodDivisor divides Periodic.period in into checkCompactInterval duration // periodDivisor divides Periodic.period in into checkCompactInterval duration
const periodDivisor = 10 const periodDivisor = 10
// Run runs periodic compactor.
func (t *Periodic) Run() { func (t *Periodic) Run() {
t.ctx, t.cancel = context.WithCancel(context.Background()) interval := t.period / time.Duration(periodDivisor)
t.revs = make([]int64, 0)
clock := t.clock
checkCompactInterval := t.period / time.Duration(periodDivisor)
go func() { go func() {
last := clock.Now() initialWait := t.clock.Now()
for { for {
t.revs = append(t.revs, t.rg.Rev()) t.revs = append(t.revs, t.rg.Rev())
select { select {
case <-t.ctx.Done(): case <-t.ctx.Done():
return return
case <-clock.After(checkCompactInterval): case <-t.clock.After(interval):
t.mu.Lock() t.mu.Lock()
p := t.paused p := t.paused
t.mu.Unlock() t.mu.Unlock()
@ -77,36 +82,44 @@ func (t *Periodic) Run() {
continue continue
} }
} }
if clock.Now().Sub(last) < t.period {
// wait up to initial given period
if t.clock.Now().Sub(initialWait) < t.period {
continue continue
} }
rev, remaining := t.getRev() rev, remaining := t.getRev()
if rev < 0 { if rev < 0 {
continue continue
} }
plog.Noticef("Starting auto-compaction at revision %d (retention: %v)", rev, t.period) plog.Noticef("Starting auto-compaction at revision %d (retention: %v)", rev, t.period)
_, err := t.c.Compact(t.ctx, &pb.CompactionRequest{Revision: rev}) _, err := t.c.Compact(t.ctx, &pb.CompactionRequest{Revision: rev})
if err == nil || err == mvcc.ErrCompacted { if err == nil || err == mvcc.ErrCompacted {
// move to next sliding window
t.revs = remaining t.revs = remaining
plog.Noticef("Finished auto-compaction at revision %d", rev) plog.Noticef("Finished auto-compaction at revision %d", rev)
} else { } else {
plog.Noticef("Failed auto-compaction at revision %d (%v)", rev, err) plog.Noticef("Failed auto-compaction at revision %d (%v)", rev, err)
plog.Noticef("Retry after %v", checkCompactInterval) plog.Noticef("Retry after %v", interval)
} }
} }
}() }()
} }
// Stop stops periodic compactor.
func (t *Periodic) Stop() { func (t *Periodic) Stop() {
t.cancel() t.cancel()
} }
// Pause pauses periodic compactor.
func (t *Periodic) Pause() { func (t *Periodic) Pause() {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
t.paused = true t.paused = true
} }
// Resume resumes periodic compactor.
func (t *Periodic) Resume() { func (t *Periodic) Resume() {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()

View File

@ -21,6 +21,7 @@ import (
pb "github.com/coreos/etcd/etcdserver/etcdserverpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/pkg/testutil" "github.com/coreos/etcd/pkg/testutil"
"github.com/jonboulle/clockwork" "github.com/jonboulle/clockwork"
) )
@ -31,12 +32,7 @@ func TestPeriodic(t *testing.T) {
fc := clockwork.NewFakeClock() fc := clockwork.NewFakeClock()
rg := &fakeRevGetter{testutil.NewRecorderStream(), 0} rg := &fakeRevGetter{testutil.NewRecorderStream(), 0}
compactable := &fakeCompactable{testutil.NewRecorderStream()} compactable := &fakeCompactable{testutil.NewRecorderStream()}
tb := &Periodic{ tb := newPeriodic(fc, retentionDuration, rg, compactable)
clock: fc,
period: retentionDuration,
rg: rg,
c: compactable,
}
tb.Run() tb.Run()
defer tb.Stop() defer tb.Stop()
@ -70,15 +66,10 @@ func TestPeriodic(t *testing.T) {
func TestPeriodicPause(t *testing.T) { func TestPeriodicPause(t *testing.T) {
fc := clockwork.NewFakeClock() fc := clockwork.NewFakeClock()
compactable := &fakeCompactable{testutil.NewRecorderStream()}
rg := &fakeRevGetter{testutil.NewRecorderStream(), 0}
retentionDuration := time.Hour retentionDuration := time.Hour
tb := &Periodic{ rg := &fakeRevGetter{testutil.NewRecorderStream(), 0}
clock: fc, compactable := &fakeCompactable{testutil.NewRecorderStream()}
period: retentionDuration, tb := newPeriodic(fc, retentionDuration, rg, compactable)
rg: rg,
c: compactable,
}
tb.Run() tb.Run()
tb.Pause() tb.Pause()

View File

@ -17,6 +17,7 @@ package compactor
import ( import (
"context" "context"
"sync" "sync"
"time"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/mvcc" "github.com/coreos/etcd/mvcc"
@ -43,25 +44,31 @@ type Revision struct {
// NewRevision creates a new instance of Revisonal compactor that purges // NewRevision creates a new instance of Revisonal compactor that purges
// the log older than retention revisions from the current revision. // the log older than retention revisions from the current revision.
func NewRevision(retention int64, rg RevGetter, c Compactable) *Revision { func NewRevision(retention int64, rg RevGetter, c Compactable) *Revision {
return &Revision{ return newRevision(clockwork.NewRealClock(), retention, rg, c)
clock: clockwork.NewRealClock(), }
func newRevision(clock clockwork.Clock, retention int64, rg RevGetter, c Compactable) *Revision {
t := &Revision{
clock: clock,
retention: retention, retention: retention,
rg: rg, rg: rg,
c: c, c: c,
} }
t.ctx, t.cancel = context.WithCancel(context.Background())
return t
} }
func (t *Revision) Run() { const revInterval = 5 * time.Minute
t.ctx, t.cancel = context.WithCancel(context.Background())
clock := t.clock
previous := int64(0)
// Run runs revision-based compactor.
func (t *Revision) Run() {
prev := int64(0)
go func() { go func() {
for { for {
select { select {
case <-t.ctx.Done(): case <-t.ctx.Done():
return return
case <-clock.After(checkCompactionInterval): case <-t.clock.After(revInterval):
t.mu.Lock() t.mu.Lock()
p := t.paused p := t.paused
t.mu.Unlock() t.mu.Unlock()
@ -71,34 +78,36 @@ func (t *Revision) Run() {
} }
rev := t.rg.Rev() - t.retention rev := t.rg.Rev() - t.retention
if rev <= 0 || rev == prev {
if rev <= 0 || rev == previous {
continue continue
} }
plog.Noticef("Starting auto-compaction at revision %d (retention: %d revisions)", rev, t.retention) plog.Noticef("Starting auto-compaction at revision %d (retention: %d revisions)", rev, t.retention)
_, err := t.c.Compact(t.ctx, &pb.CompactionRequest{Revision: rev}) _, err := t.c.Compact(t.ctx, &pb.CompactionRequest{Revision: rev})
if err == nil || err == mvcc.ErrCompacted { if err == nil || err == mvcc.ErrCompacted {
previous = rev prev = rev
plog.Noticef("Finished auto-compaction at revision %d", rev) plog.Noticef("Finished auto-compaction at revision %d", rev)
} else { } else {
plog.Noticef("Failed auto-compaction at revision %d (%v)", rev, err) plog.Noticef("Failed auto-compaction at revision %d (%v)", rev, err)
plog.Noticef("Retry after %v", checkCompactionInterval) plog.Noticef("Retry after %v", revInterval)
} }
} }
}() }()
} }
// Stop stops revision-based compactor.
func (t *Revision) Stop() { func (t *Revision) Stop() {
t.cancel() t.cancel()
} }
// Pause pauses revision-based compactor.
func (t *Revision) Pause() { func (t *Revision) Pause() {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
t.paused = true t.paused = true
} }
// Resume resumes revision-based compactor.
func (t *Revision) Resume() { func (t *Revision) Resume() {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()

View File

@ -21,6 +21,7 @@ import (
pb "github.com/coreos/etcd/etcdserver/etcdserverpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/pkg/testutil" "github.com/coreos/etcd/pkg/testutil"
"github.com/jonboulle/clockwork" "github.com/jonboulle/clockwork"
) )
@ -28,23 +29,18 @@ func TestRevision(t *testing.T) {
fc := clockwork.NewFakeClock() fc := clockwork.NewFakeClock()
rg := &fakeRevGetter{testutil.NewRecorderStream(), 0} rg := &fakeRevGetter{testutil.NewRecorderStream(), 0}
compactable := &fakeCompactable{testutil.NewRecorderStream()} compactable := &fakeCompactable{testutil.NewRecorderStream()}
tb := &Revision{ tb := newRevision(fc, 10, rg, compactable)
clock: fc,
retention: 10,
rg: rg,
c: compactable,
}
tb.Run() tb.Run()
defer tb.Stop() defer tb.Stop()
fc.Advance(checkCompactionInterval) fc.Advance(revInterval)
rg.Wait(1) rg.Wait(1)
// nothing happens // nothing happens
rg.SetRev(99) // will be 100 rg.SetRev(99) // will be 100
expectedRevision := int64(90) expectedRevision := int64(90)
fc.Advance(checkCompactionInterval) fc.Advance(revInterval)
rg.Wait(1) rg.Wait(1)
a, err := compactable.Wait(1) a, err := compactable.Wait(1)
if err != nil { if err != nil {
@ -61,7 +57,7 @@ func TestRevision(t *testing.T) {
rg.SetRev(199) // will be 200 rg.SetRev(199) // will be 200
expectedRevision = int64(190) expectedRevision = int64(190)
fc.Advance(checkCompactionInterval) fc.Advance(revInterval)
rg.Wait(1) rg.Wait(1)
a, err = compactable.Wait(1) a, err = compactable.Wait(1)
if err != nil { if err != nil {
@ -74,22 +70,17 @@ func TestRevision(t *testing.T) {
func TestRevisionPause(t *testing.T) { func TestRevisionPause(t *testing.T) {
fc := clockwork.NewFakeClock() fc := clockwork.NewFakeClock()
compactable := &fakeCompactable{testutil.NewRecorderStream()}
rg := &fakeRevGetter{testutil.NewRecorderStream(), 99} // will be 100 rg := &fakeRevGetter{testutil.NewRecorderStream(), 99} // will be 100
tb := &Revision{ compactable := &fakeCompactable{testutil.NewRecorderStream()}
clock: fc, tb := newRevision(fc, 10, rg, compactable)
retention: 10,
rg: rg,
c: compactable,
}
tb.Run() tb.Run()
tb.Pause() tb.Pause()
// tb will collect 3 hours of revisions but not compact since paused // tb will collect 3 hours of revisions but not compact since paused
n := int(time.Hour / checkCompactionInterval) n := int(time.Hour / revInterval)
for i := 0; i < 3*n; i++ { for i := 0; i < 3*n; i++ {
fc.Advance(checkCompactionInterval) fc.Advance(revInterval)
} }
// tb ends up waiting for the clock // tb ends up waiting for the clock
@ -103,7 +94,7 @@ func TestRevisionPause(t *testing.T) {
tb.Resume() tb.Resume()
// unblock clock, will kick off a compaction at hour 3:05 // unblock clock, will kick off a compaction at hour 3:05
fc.Advance(checkCompactionInterval) fc.Advance(revInterval)
rg.Wait(1) rg.Wait(1)
a, err := compactable.Wait(1) a, err := compactable.Wait(1)
if err != nil { if err != nil {