WIP runtime-controllable concurrency for replication
Changes done so far:
- signal to route concurrency request up to the stepQueue
- pretty hacky, no status reporting yet
- stepQueue upsizing (confirmed that it works, no intermediary commit)
Stuck at: stepQueue downsizing
- idea was to have the stepQueue signal to the activated step that it
should suspend
- ideally, we'd just kill everything associated with the step and track
it as restartable
- the driver model doesn't allow for that though within an attempt
- would need to start a separate run for the step
- less perfect: tell the downsized steps to stop copying, but leave
all zfs sends + rpc conns open
- - doesn't give back the resoures that a step aquired before
being selected as downsize victim (open zfs processe + TCP conn,
some memory in the pipe)
- - looks weird to user if the ps aux
- + re-waking a step is easy: just tell it to proceed with copying
- (the impl would likely pass a check function down into Step.do
and have it check that functino periodically. the suspend should
be acknowledged, and stepQueue should only remove the step from
the active queue _after_ that step has acknowledged that is
suspended)
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -47,6 +48,8 @@ func (j *controlJob) OwnedDatasetSubtreeRoot() (p *zfs.DatasetPath, ok bool) { r
|
||||
|
||||
func (j *controlJob) SenderConfig() *endpoint.SenderConfig { return nil }
|
||||
|
||||
func (j *controlJob) SetConcurrency(concurrency int) error { return errors.Errorf("not supported") }
|
||||
|
||||
var promControl struct {
|
||||
requestBegin *prometheus.CounterVec
|
||||
requestFinished *prometheus.HistogramVec
|
||||
@@ -126,6 +129,7 @@ func (j *controlJob) Run(ctx context.Context) {
|
||||
type reqT struct {
|
||||
Name string
|
||||
Op string
|
||||
Data string
|
||||
}
|
||||
var req reqT
|
||||
if decoder(&req) != nil {
|
||||
@@ -138,6 +142,14 @@ func (j *controlJob) Run(ctx context.Context) {
|
||||
err = j.jobs.wakeup(req.Name)
|
||||
case "reset":
|
||||
err = j.jobs.reset(req.Name)
|
||||
case "set-concurrency":
|
||||
var concurrency int
|
||||
concurrency, err = strconv.Atoi(req.Data) // shadow
|
||||
if err != nil {
|
||||
// fallthrough outer
|
||||
} else {
|
||||
err = j.jobs.setConcurrency(req.Name, concurrency)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("operation %q is invalid", req.Op)
|
||||
}
|
||||
|
||||
@@ -176,6 +176,18 @@ func (s *jobs) reset(job string) error {
|
||||
return wu()
|
||||
}
|
||||
|
||||
func (s *jobs) setConcurrency(jobName string, concurrency int) error {
|
||||
s.m.RLock()
|
||||
defer s.m.RUnlock()
|
||||
|
||||
job, ok := s.jobs[jobName]
|
||||
if !ok {
|
||||
return errors.Errorf("Job %q does not exist", job)
|
||||
}
|
||||
|
||||
return job.SetConcurrency(concurrency)
|
||||
}
|
||||
|
||||
const (
|
||||
jobNamePrometheus = "_prometheus"
|
||||
jobNameControl = "_control"
|
||||
|
||||
+30
-6
@@ -55,9 +55,12 @@ const (
|
||||
type activeSideTasks struct {
|
||||
state ActiveSideState
|
||||
|
||||
concurrency int
|
||||
|
||||
// valid for state ActiveSideReplicating, ActiveSidePruneSender, ActiveSidePruneReceiver, ActiveSideDone
|
||||
replicationReport driver.ReportFunc
|
||||
replicationCancel context.CancelFunc
|
||||
replicationReport driver.ReportFunc
|
||||
replicationCancel context.CancelFunc
|
||||
replicationSetConcurrency driver.SetConcurrencyFunc
|
||||
|
||||
// valid for state ActiveSidePruneSender, ActiveSidePruneReceiver, ActiveSideDone
|
||||
prunerSender, prunerReceiver *pruner.Pruner
|
||||
@@ -278,6 +281,8 @@ func activeSide(g *config.Global, in *config.ActiveJob, configJob interface{}) (
|
||||
return nil, errors.Wrap(err, "invalid job name")
|
||||
}
|
||||
|
||||
j.tasks.concurrency = 1 // FIXME
|
||||
|
||||
switch v := configJob.(type) {
|
||||
case *config.PushJob:
|
||||
j.mode, err = modePushFromConfig(g, v, j.name) // shadow
|
||||
@@ -375,6 +380,22 @@ func (j *ActiveSide) SenderConfig() *endpoint.SenderConfig {
|
||||
return push.senderConfig
|
||||
}
|
||||
|
||||
func (j *ActiveSide) SetConcurrency(concurrency int) (err error) {
|
||||
j.updateTasks(func(tasks *activeSideTasks) {
|
||||
if tasks.replicationSetConcurrency != nil {
|
||||
err = tasks.replicationSetConcurrency(concurrency) // no shadow
|
||||
if err == nil {
|
||||
tasks.concurrency = concurrency
|
||||
}
|
||||
} else {
|
||||
// FIXME this is not great, should always be able to set it
|
||||
err = errors.Errorf("cannot set while not replicating")
|
||||
}
|
||||
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (j *ActiveSide) Run(ctx context.Context) {
|
||||
log := GetLogger(ctx)
|
||||
ctx = logging.WithSubsystemLoggers(ctx, log)
|
||||
@@ -436,11 +457,14 @@ func (j *ActiveSide) do(ctx context.Context) {
|
||||
ctx, repCancel := context.WithCancel(ctx)
|
||||
var repWait driver.WaitFunc
|
||||
j.updateTasks(func(tasks *activeSideTasks) {
|
||||
// reset it
|
||||
*tasks = activeSideTasks{}
|
||||
// reset it (almost)
|
||||
old := *tasks
|
||||
*tasks = activeSideTasks{
|
||||
concurrency: old.concurrency,
|
||||
}
|
||||
tasks.replicationCancel = repCancel
|
||||
tasks.replicationReport, repWait = replication.Do(
|
||||
ctx, logic.NewPlanner(j.promRepStateSecs, j.promBytesReplicated, sender, receiver, j.mode.PlannerPolicy()),
|
||||
tasks.replicationReport, repWait, tasks.replicationSetConcurrency = replication.Do(
|
||||
ctx, tasks.concurrency, logic.NewPlanner(j.promRepStateSecs, j.promBytesReplicated, sender, receiver, j.mode.PlannerPolicy()),
|
||||
)
|
||||
tasks.state = ActiveSideReplicating
|
||||
})
|
||||
|
||||
@@ -40,6 +40,7 @@ type Job interface {
|
||||
// must return the root of that subtree as rfs and ok = true
|
||||
OwnedDatasetSubtreeRoot() (rfs *zfs.DatasetPath, ok bool)
|
||||
SenderConfig() *endpoint.SenderConfig
|
||||
SetConcurrency(concurrency int) error
|
||||
}
|
||||
|
||||
type Type string
|
||||
|
||||
@@ -163,6 +163,8 @@ func (j *PassiveSide) SenderConfig() *endpoint.SenderConfig {
|
||||
|
||||
func (*PassiveSide) RegisterMetrics(registerer prometheus.Registerer) {}
|
||||
|
||||
func (*PassiveSide) SetConcurrency(concurrency int) error { return errors.Errorf("not supported") }
|
||||
|
||||
func (j *PassiveSide) Run(ctx context.Context) {
|
||||
|
||||
log := GetLogger(ctx)
|
||||
|
||||
@@ -117,6 +117,8 @@ outer:
|
||||
}
|
||||
}
|
||||
|
||||
func (*SnapJob) SetConcurrency(concurrency int) error { return errors.Errorf("not supported") }
|
||||
|
||||
// Adaptor that implements pruner.History around a pruner.Target.
|
||||
// The ReplicationCursor method is Get-op only and always returns
|
||||
// the filesystem's most recent version's GUID.
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
@@ -51,6 +52,8 @@ func (j *prometheusJob) OwnedDatasetSubtreeRoot() (p *zfs.DatasetPath, ok bool)
|
||||
|
||||
func (j *prometheusJob) SenderConfig() *endpoint.SenderConfig { return nil }
|
||||
|
||||
func (j *prometheusJob) SetConcurrency(concurrency int) error { return errors.Errorf("not supported") }
|
||||
|
||||
func (j *prometheusJob) RegisterMetrics(registerer prometheus.Registerer) {}
|
||||
|
||||
func (j *prometheusJob) Run(ctx context.Context) {
|
||||
|
||||
Reference in New Issue
Block a user