|
@@ -15,14 +15,13 @@
|
|
|
package compactor
|
|
package compactor
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
- "sync"
|
|
|
|
|
|
|
+ "fmt"
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
- pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
|
|
|
|
- "github.com/coreos/etcd/mvcc"
|
|
|
|
|
"github.com/coreos/pkg/capnslog"
|
|
"github.com/coreos/pkg/capnslog"
|
|
|
- "github.com/jonboulle/clockwork"
|
|
|
|
|
"golang.org/x/net/context"
|
|
"golang.org/x/net/context"
|
|
|
|
|
+
|
|
|
|
|
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
var (
|
|
@@ -32,8 +31,24 @@ var (
|
|
|
const (
|
|
const (
|
|
|
checkCompactionInterval = 5 * time.Minute
|
|
checkCompactionInterval = 5 * time.Minute
|
|
|
executeCompactionInterval = time.Hour
|
|
executeCompactionInterval = time.Hour
|
|
|
|
|
+
|
|
|
|
|
+ ModePeriodic = "periodic"
|
|
|
|
|
+ ModeRevision = "revision"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+// Compactor purges old log from the storage periodically.
|
|
|
|
|
+type Compactor interface {
|
|
|
|
|
+ // Run starts the main loop of the compactor in background.
|
|
|
|
|
+ // Use Stop() to halt the loop and release the resource.
|
|
|
|
|
+ Run()
|
|
|
|
|
+ // Stop halts the main loop of the compactor.
|
|
|
|
|
+ Stop()
|
|
|
|
|
+ // Pause temporally suspend the compactor not to run compaction. Resume() to unpose.
|
|
|
|
|
+ Pause()
|
|
|
|
|
+ // Resume restarts the compactor suspended by Pause().
|
|
|
|
|
+ Resume()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
type Compactable interface {
|
|
type Compactable interface {
|
|
|
Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)
|
|
Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)
|
|
|
}
|
|
}
|
|
@@ -42,96 +57,13 @@ type RevGetter interface {
|
|
|
Rev() int64
|
|
Rev() int64
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Periodic compacts the log by purging revisions older than
|
|
|
|
|
-// the configured retention time. Compaction happens hourly.
|
|
|
|
|
-type Periodic struct {
|
|
|
|
|
- clock clockwork.Clock
|
|
|
|
|
- periodInHour int
|
|
|
|
|
-
|
|
|
|
|
- rg RevGetter
|
|
|
|
|
- c Compactable
|
|
|
|
|
-
|
|
|
|
|
- revs []int64
|
|
|
|
|
- ctx context.Context
|
|
|
|
|
- cancel context.CancelFunc
|
|
|
|
|
-
|
|
|
|
|
- mu sync.Mutex
|
|
|
|
|
- paused bool
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func NewPeriodic(h int, rg RevGetter, c Compactable) *Periodic {
|
|
|
|
|
- return &Periodic{
|
|
|
|
|
- clock: clockwork.NewRealClock(),
|
|
|
|
|
- periodInHour: h,
|
|
|
|
|
- rg: rg,
|
|
|
|
|
- c: c,
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (t *Periodic) Run() {
|
|
|
|
|
- t.ctx, t.cancel = context.WithCancel(context.Background())
|
|
|
|
|
- t.revs = make([]int64, 0)
|
|
|
|
|
- clock := t.clock
|
|
|
|
|
-
|
|
|
|
|
- go func() {
|
|
|
|
|
- last := clock.Now()
|
|
|
|
|
- for {
|
|
|
|
|
- t.revs = append(t.revs, t.rg.Rev())
|
|
|
|
|
- select {
|
|
|
|
|
- case <-t.ctx.Done():
|
|
|
|
|
- return
|
|
|
|
|
- case <-clock.After(checkCompactionInterval):
|
|
|
|
|
- t.mu.Lock()
|
|
|
|
|
- p := t.paused
|
|
|
|
|
- t.mu.Unlock()
|
|
|
|
|
- if p {
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if clock.Now().Sub(last) < executeCompactionInterval {
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- rev, remaining := t.getRev(t.periodInHour)
|
|
|
|
|
- if rev < 0 {
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- plog.Noticef("Starting auto-compaction at revision %d", rev)
|
|
|
|
|
- _, err := t.c.Compact(t.ctx, &pb.CompactionRequest{Revision: rev})
|
|
|
|
|
- if err == nil || err == mvcc.ErrCompacted {
|
|
|
|
|
- t.revs = remaining
|
|
|
|
|
- last = clock.Now()
|
|
|
|
|
- plog.Noticef("Finished auto-compaction at revision %d", rev)
|
|
|
|
|
- } else {
|
|
|
|
|
- plog.Noticef("Failed auto-compaction at revision %d (%v)", err, rev)
|
|
|
|
|
- plog.Noticef("Retry after %v", checkCompactionInterval)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (t *Periodic) Stop() {
|
|
|
|
|
- t.cancel()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (t *Periodic) Pause() {
|
|
|
|
|
- t.mu.Lock()
|
|
|
|
|
- defer t.mu.Unlock()
|
|
|
|
|
- t.paused = true
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (t *Periodic) Resume() {
|
|
|
|
|
- t.mu.Lock()
|
|
|
|
|
- defer t.mu.Unlock()
|
|
|
|
|
- t.paused = false
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (t *Periodic) getRev(h int) (int64, []int64) {
|
|
|
|
|
- i := len(t.revs) - int(time.Duration(h)*time.Hour/checkCompactionInterval)
|
|
|
|
|
- if i < 0 {
|
|
|
|
|
- return -1, t.revs
|
|
|
|
|
|
|
+func New(mode string, retention int, rg RevGetter, c Compactable) (Compactor, error) {
|
|
|
|
|
+ switch mode {
|
|
|
|
|
+ case ModePeriodic:
|
|
|
|
|
+ return NewPeriodic(retention, rg, c), nil
|
|
|
|
|
+ case ModeRevision:
|
|
|
|
|
+ return NewRevision(int64(retention), rg, c), nil
|
|
|
|
|
+ default:
|
|
|
|
|
+ return nil, fmt.Errorf("unsupported compaction mode %s", mode)
|
|
|
}
|
|
}
|
|
|
- return t.revs[i], t.revs[i+1:]
|
|
|
|
|
}
|
|
}
|