zrepl/daemon/job/snapjob.go

130 lines
3.2 KiB
Go
Raw Normal View History

package job
import (
"context"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/zrepl/zrepl/config"
"github.com/zrepl/zrepl/daemon/filters"
"github.com/zrepl/zrepl/daemon/job/wakeup"
"github.com/zrepl/zrepl/daemon/logging"
"github.com/zrepl/zrepl/daemon/pruner"
"github.com/zrepl/zrepl/daemon/snapper"
"github.com/zrepl/zrepl/endpoint"
"github.com/zrepl/zrepl/zfs"
)
type SnapJob struct {
name string
fsfilter zfs.DatasetFilter
snapper *snapper.PeriodicOrManual
prunerFactory *pruner.SinglePrunerFactory
promPruneSecs *prometheus.HistogramVec // no labels!
pruner *pruner.Pruner
}
2018-11-21 02:42:13 +01:00
func (j *SnapJob) Name() string { return j.name }
2018-11-21 03:39:03 +01:00
func (j *SnapJob) getPruner(ctx context.Context, sender *endpoint.Sender) (*pruner.Pruner) {
p := j.prunerFactory.BuildSinglePruner(ctx,sender,sender)
return p
}
func (j *SnapJob) Type() Type { return TypeSnap }
func (j *SnapJob) RunPeriodic(ctx context.Context, wakeUpCommon chan <- struct{}) {
j.snapper.Run(ctx, wakeUpCommon)
}
func (j *SnapJob) FSFilter() zfs.DatasetFilter {
return j.fsfilter
}
func snapJob(g *config.Global, in *config.SnapJob) (j *SnapJob, err error) {
j = &SnapJob{}
fsf, err := filters.DatasetMapFilterFromConfig(in.Filesystems)
if err != nil {
return nil, errors.Wrap(err, "cannnot build filesystem filter")
}
j.fsfilter = fsf
if j.snapper, err = snapper.FromConfig(g, fsf, in.Snapshotting); err != nil {
return nil, errors.Wrap(err, "cannot build snapper")
}
j.name = in.Name
j.promPruneSecs = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "zrepl",
Subsystem: "pruning",
Name: "time",
Help: "seconds spent in pruner",
ConstLabels: prometheus.Labels{"zrepl_job":j.name},
}, []string {})
j.prunerFactory, err = pruner.NewSinglePrunerFactory(in.Pruning, j.promPruneSecs)
if err != nil {
return nil, errors.Wrap(err, "cannot build snapjob pruning rules")
}
return j, nil
}
func (j *SnapJob) RegisterMetrics(registerer prometheus.Registerer) {
registerer.MustRegister(j.promPruneSecs)
}
2018-11-21 02:08:39 +01:00
type SnapJobStatus struct {
Pruning *pruner.Report
}
func (j *SnapJob) Status() *Status {
2018-11-21 02:08:39 +01:00
s := &SnapJobStatus{}
t := j.Type()
2018-11-21 02:42:13 +01:00
if j.pruner != nil {
s.Pruning = j.pruner.Report()
}
return &Status{Type: t, JobSpecific: s}
}
func (j *SnapJob) Run(ctx context.Context) {
log := GetLogger(ctx)
ctx = logging.WithSubsystemLoggers(ctx, log)
defer log.Info("job exiting")
periodicDone := make(chan struct{})
ctx, cancel := context.WithCancel(ctx)
defer cancel()
go j.RunPeriodic(ctx, periodicDone)
invocationCount := 0
outer:
for {
log.Info("wait for wakeups")
select {
case <-ctx.Done():
log.WithError(ctx.Err()).Info("context")
break outer
case <-wakeup.Wait(ctx):
case <-periodicDone:
}
invocationCount++
invLog := log.WithField("invocation", invocationCount)
2018-11-21 02:42:13 +01:00
j.doPrune(WithLogger(ctx, invLog))
}
}
2018-11-21 02:42:13 +01:00
func (j *SnapJob) doPrune(ctx context.Context) {
log := GetLogger(ctx)
ctx = logging.WithSubsystemLoggers(ctx, log)
2018-11-21 02:42:13 +01:00
sender := endpoint.NewSender(j.FSFilter())
2018-11-21 03:39:03 +01:00
j.pruner = j.getPruner(ctx, sender)
2018-11-21 02:42:13 +01:00
log.Info("start pruning")
j.pruner.Prune()
log.Info("finished pruning")
}