WIP: recurring jobs

Done:

* implement autosnapper that asserts interval between snapshots
* implement pruner

* job pull: pulling + pruning
* job source: autosnapping + serving

TODO

* job source: pruning
* job local: everything
* fatal errors such as serve that cannot bind socket must be more
visible
* couldn't things that need a snapshotprefix just use a interface
Prefixer() instead? then we could have prefixsnapshotfilter and not
duplicate it every time...
* either go full context.Context or not at all...? just wait because
community climate around it isn't that great and we only need it for
cancellation? roll our own?
This commit is contained in:
Christian Schwarz
2017-09-13 23:27:18 +02:00
parent c6ca1efaae
commit e70b6f3071
8 changed files with 425 additions and 45 deletions
+45 -17
View File
@@ -1,14 +1,13 @@
package cmd
import (
"context"
mapstructure "github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/zrepl/zrepl/rpc"
"github.com/zrepl/zrepl/util"
"io"
"os"
"os/signal"
"syscall"
"sync"
"time"
)
@@ -16,10 +15,13 @@ type SourceJob struct {
Name string
Serve AuthenticatedChannelListenerFactory
Datasets *DatasetMapFilter
SnapshotFilter *PrefixSnapshotFilter
SnapshotPrefix string
Interval time.Duration
Prune PrunePolicy
Debug JobDebugSettings
snapCancel context.CancelFunc
serveCancel context.CancelFunc
}
func parseSourceJob(name string, i map[string]interface{}) (j *SourceJob, err error) {
@@ -48,7 +50,7 @@ func parseSourceJob(name string, i map[string]interface{}) (j *SourceJob, err er
return
}
if j.SnapshotFilter, err = parsePrefixSnapshotFilter(asMap.SnapshotPrefix); err != nil {
if j.SnapshotPrefix, err = parseSnapshotPrefix(asMap.SnapshotPrefix); err != nil {
return
}
@@ -74,16 +76,46 @@ func (j *SourceJob) JobName() string {
return j.Name
}
func (j *SourceJob) JobDo(log Logger) (err error) {
func (j *SourceJob) JobStart(ctx context.Context) {
var wg sync.WaitGroup
log := ctx.Value(contextKeyLog).(Logger)
log.Printf("starting autosnap")
var snapContext context.Context
snapContext, j.snapCancel = context.WithCancel(ctx)
snapContext = context.WithValue(snapContext, contextKeyLog, util.NewPrefixLogger(log, "autosnap"))
a := IntervalAutosnap{DatasetFilter: j.Datasets, Prefix: j.SnapshotPrefix, SnapshotInterval: j.Interval}
wg.Add(1)
go func() {
a.Run(snapContext)
wg.Done()
}()
log.Printf("starting serve")
var serveContext context.Context
serveContext, j.serveCancel = context.WithCancel(ctx)
serveContext = context.WithValue(serveContext, contextKeyLog, util.NewPrefixLogger(log, "serve"))
wg.Add(1)
go func() {
j.serve(serveContext)
wg.Done()
}()
wg.Wait()
}
func (j *SourceJob) serve(ctx context.Context) {
log := ctx.Value(contextKeyLog).(Logger)
listener, err := j.Serve.Listen()
if err != nil {
return err
log.Printf("error listening: %s", err)
return
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
rwcChan := make(chan io.ReadWriteCloser)
// Serve connections until interrupted or error
@@ -131,24 +163,20 @@ outer:
}
rwc.Close()
case sig := <-sigChan:
log.Printf("%s received", sig)
case <-ctx.Done():
log.Printf("context: %s", ctx.Err())
break outer
}
}
signal.Stop(sigChan)
close(sigChan)
log.Printf("closing listener")
err = listener.Close()
if err != nil {
log.Printf("error closing listener: %s", err)
}
return nil
return
}