Files
fn-serverless/api/runnerpool/naive_placer.go
Tolga Ceylan 850508a9bd fn: naive placer rr fixup with tests (#1566)
If naive placer is not instantiated per call/runner group (aka LBG),
then the rr index will not trigger an round-robin behavior since
the index is initialized and stored in the placer configuration.

With this PR, moving rr index to per RunnerPool.Runners() inner loop
to ensure a round robin within that set. Each time we fetch a set,
since the set might be different, we reset our rr index. This means
we rr within that set once, then randomly start from another node
for the next RunnerPool.Runners() iteration. In busy systems,
no significant behavior change is expected (accept the removal of
atomic operations with respect to performance), but in idle systems
round robin behavior should be more observable and simple to follow and can
reduce same hit cases for the given RunnerPool.Runners().

In addition, introducing naive placer tests to ensure we observe
this behavior.
2019-11-04 10:21:13 -08:00

64 lines
1.3 KiB
Go

package runnerpool
import (
"context"
"time"
"github.com/fnproject/fn/api/models"
"github.com/sirupsen/logrus"
)
type naivePlacer struct {
cfg PlacerConfig
}
func NewNaivePlacer(cfg *PlacerConfig) Placer {
logrus.Infof("Creating new naive runnerpool placer with config=%+v", cfg)
return &naivePlacer{
cfg: *cfg,
}
}
func (sp *naivePlacer) GetPlacerConfig() PlacerConfig {
return sp.cfg
}
func (sp *naivePlacer) PlaceCall(ctx context.Context, rp RunnerPool, call RunnerCall) error {
state := NewPlacerTracker(ctx, &sp.cfg, call)
defer state.HandleDone()
var runnerPoolErr error
for {
var runners []Runner
runners, runnerPoolErr = rp.Runners(ctx, call)
rrIndex := uint64(time.Now().Nanosecond())
for j := 0; j < len(runners) && !state.IsDone(); j++ {
rrIndex += 1
r := runners[rrIndex%uint64(len(runners))]
placed, err := state.TryRunner(r, call)
if placed {
return err
}
}
if !state.RetryAllBackoff(len(runners), runnerPoolErr) {
break
}
}
if runnerPoolErr != nil {
// If we haven't been able to place the function and we got an error
// from the runner pool, return that error (since we don't have
// enough runners to handle the current load and the runner pool is
// having trouble).
state.HandleFindRunnersFailure(runnerPoolErr)
return runnerPoolErr
}
return models.ErrCallTimeoutServerBusy
}