faas/gateway/handlers/scaling.go
Alex Ellis (VMware) 9512f09d2b Refactor scaling to use existing code
Existing code has been used for scaling up and querying replicas.
This meant the new code was deleted and there is less duplication
now.

The cache store a whole query response rather than just the
available replica count and the tests were updated. This has been
tested with Docker swarm and the image:
 openfaas/gateway:scale-17-07-2018

This feature now needs the env-var of scale_from_zero to be enabled
in order to turn on the scaling behaviour.

Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
2018-07-20 11:39:36 +01:00

91 lines
2.3 KiB
Go

// Copyright (c) OpenFaaS Project. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package handlers
import (
"fmt"
"log"
"net/http"
"time"
)
// ScalingConfig for scaling behaviours
type ScalingConfig struct {
MaxPollCount uint
FunctionPollInterval time.Duration
CacheExpiry time.Duration
ServiceQuery ServiceQuery
}
// MakeScalingHandler creates handler which can scale a function from
// zero to 1 replica(s).
func MakeScalingHandler(next http.HandlerFunc, upstream http.HandlerFunc, config ScalingConfig) http.HandlerFunc {
cache := FunctionCache{
Cache: make(map[string]*FunctionMeta),
Expiry: config.CacheExpiry,
}
return func(w http.ResponseWriter, r *http.Request) {
functionName := getServiceName(r.URL.String())
if serviceQueryResponse, hit := cache.Get(functionName); hit && serviceQueryResponse.AvailableReplicas > 0 {
next.ServeHTTP(w, r)
return
}
queryResponse, err := config.ServiceQuery.GetReplicas(functionName)
cache.Set(functionName, queryResponse)
if err != nil {
var errStr string
errStr = fmt.Sprintf("error finding function %s: %s", functionName, err.Error())
log.Printf(errStr)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(errStr))
return
}
if queryResponse.AvailableReplicas == 0 {
minReplicas := uint64(1)
if queryResponse.MinReplicas > 0 {
minReplicas = queryResponse.MinReplicas
}
err := config.ServiceQuery.SetReplicas(functionName, minReplicas)
if err != nil {
errStr := fmt.Errorf("unable to scale function [%s], err: %s", functionName, err)
log.Printf(errStr.Error())
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(errStr.Error()))
return
}
for i := 0; i < int(config.MaxPollCount); i++ {
queryResponse, err := config.ServiceQuery.GetReplicas(functionName)
cache.Set(functionName, queryResponse)
if err != nil {
errStr := fmt.Sprintf("error: %s", err.Error())
log.Printf(errStr)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(errStr))
return
}
if queryResponse.AvailableReplicas > 0 {
break
}
time.Sleep(config.FunctionPollInterval)
}
}
next.ServeHTTP(w, r)
}
}