Feature request per pod (#1946)
This feature enables routing more than one request to a pod at the same time. This is the first draft of the work and might involve more optimizations later.
This commit is contained in:
+58
-40
@@ -50,25 +50,60 @@ func (executor *Executor) getServiceForFunctionAPI(w http.ResponseWriter, r *htt
|
||||
}
|
||||
|
||||
t := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
|
||||
et, exists := executor.executorTypes[t]
|
||||
if !exists {
|
||||
http.Error(w, fmt.Sprintf("Unknown executor type '%v'", t), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
et := executor.executorTypes[t]
|
||||
|
||||
executor.logger.Debug(fmt.Sprintf("active instances: %v", et.GetTotalAvailable(fn)))
|
||||
conncurrency := fn.Spec.Concurrency
|
||||
if conncurrency == 0 {
|
||||
// set to default conncurrency
|
||||
conncurrency = 5
|
||||
executor.logger.Debug(fmt.Sprintf("concurrency specified in function: %v", fn.Spec.Concurrency))
|
||||
executor.logger.Debug("setting concurrency to 5")
|
||||
}
|
||||
if t == fv1.ExecutorTypePoolmgr && et.GetTotalAvailable(fn) >= conncurrency {
|
||||
errMsg := fmt.Sprintf("max concurrency reached for %v. All %v instance are active", fn.ObjectMeta.Name, conncurrency)
|
||||
executor.logger.Error("error occurred", zap.String("error", errMsg))
|
||||
http.Error(w, errMsg, http.StatusTooManyRequests)
|
||||
return
|
||||
// Check function -> svc cache
|
||||
executor.logger.Debug("checking for cached function service",
|
||||
zap.String("function_name", fn.ObjectMeta.Name),
|
||||
zap.String("function_namespace", fn.ObjectMeta.Namespace))
|
||||
if t == fv1.ExecutorTypePoolmgr {
|
||||
concurrency := fn.Spec.Concurrency
|
||||
if concurrency == 0 {
|
||||
concurrency = 5
|
||||
}
|
||||
requestsPerpod := fn.Spec.RequestsPerPod
|
||||
if requestsPerpod == 0 {
|
||||
requestsPerpod = 1
|
||||
}
|
||||
fsvc, active, err := et.GetFuncSvcFromPoolCache(fn, requestsPerpod)
|
||||
// check if its a cache hit (check if there is already specialized function pod that can serve another request)
|
||||
if err == nil {
|
||||
// if a pod is already serving request then it already exists else validated
|
||||
executor.logger.Debug("from cache", zap.Int("active", active))
|
||||
if active > 1 || et.IsValid(fsvc) {
|
||||
// Cached, return svc address
|
||||
executor.logger.Debug("served from cache", zap.String("name", fsvc.Name), zap.String("address", fsvc.Address))
|
||||
executor.writeResponse(w, fsvc.Address, fn.ObjectMeta.Name)
|
||||
return
|
||||
}
|
||||
executor.logger.Debug("deleting cache entry for invalid address",
|
||||
zap.String("function_name", fn.ObjectMeta.Name),
|
||||
zap.String("function_namespace", fn.ObjectMeta.Namespace),
|
||||
zap.String("address", fsvc.Address))
|
||||
et.DeleteFuncSvcFromCache(fsvc)
|
||||
active--
|
||||
}
|
||||
|
||||
if active >= concurrency {
|
||||
errMsg := fmt.Sprintf("max concurrency reached for %v. All %v instance are active", fn.ObjectMeta.Name, concurrency)
|
||||
executor.logger.Error("error occurred", zap.String("error", errMsg))
|
||||
http.Error(w, errMsg, http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fsvc, err := et.GetFuncSvcFromCache(fn)
|
||||
if err == nil {
|
||||
if et.IsValid(fsvc) {
|
||||
// Cached, return svc address
|
||||
executor.writeResponse(w, fsvc.Address, fn.ObjectMeta.Name)
|
||||
return
|
||||
}
|
||||
executor.logger.Debug("deleting cache entry for invalid address",
|
||||
zap.String("function_name", fn.ObjectMeta.Name),
|
||||
zap.String("function_namespace", fn.ObjectMeta.Namespace),
|
||||
zap.String("address", fsvc.Address))
|
||||
et.DeleteFuncSvcFromCache(fsvc)
|
||||
}
|
||||
}
|
||||
|
||||
serviceName, err := executor.getServiceForFunction(fn)
|
||||
@@ -81,12 +116,15 @@ func (executor *Executor) getServiceForFunctionAPI(w http.ResponseWriter, r *htt
|
||||
http.Error(w, msg, code)
|
||||
return
|
||||
}
|
||||
executor.writeResponse(w, serviceName, fn.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(serviceName))
|
||||
func (executor *Executor) writeResponse(w http.ResponseWriter, serviceName string, fnName string) {
|
||||
_, err := w.Write([]byte(serviceName))
|
||||
if err != nil {
|
||||
executor.logger.Error(
|
||||
"error writing HTTP response",
|
||||
zap.String("function", fn.ObjectMeta.Name),
|
||||
zap.String("function", fnName),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
@@ -102,26 +140,6 @@ func (executor *Executor) getServiceForFunctionAPI(w http.ResponseWriter, r *htt
|
||||
// To make it optimal, plan is to add an eager cache invalidator function that watches for pod deletion events and
|
||||
// invalidates the cache entry if the pod address was cached.
|
||||
func (executor *Executor) getServiceForFunction(fn *fv1.Function) (string, error) {
|
||||
t := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
|
||||
et := executor.executorTypes[t]
|
||||
// Check function -> svc cache
|
||||
executor.logger.Debug("checking for cached function service",
|
||||
zap.String("function_name", fn.ObjectMeta.Name),
|
||||
zap.String("function_namespace", fn.ObjectMeta.Namespace))
|
||||
|
||||
fsvc, err := et.GetFuncSvcFromCache(fn)
|
||||
if err == nil {
|
||||
if et.IsValid(fsvc) {
|
||||
// Cached, return svc address
|
||||
return fsvc.Address, nil
|
||||
}
|
||||
executor.logger.Debug("deleting cache entry for invalid address",
|
||||
zap.String("function_name", fn.ObjectMeta.Name),
|
||||
zap.String("function_namespace", fn.ObjectMeta.Namespace),
|
||||
zap.String("address", fsvc.Address))
|
||||
et.DeleteFuncSvcFromCache(fsvc)
|
||||
}
|
||||
|
||||
respChan := make(chan *createFuncServiceResponse)
|
||||
executor.requestChan <- &createFuncServiceRequest{
|
||||
function: fn,
|
||||
|
||||
@@ -238,7 +238,7 @@ func serveMetric(logger *zap.Logger) {
|
||||
// StartExecutor Starts executor and the executor components such as Poolmgr,
|
||||
// deploymgr and potential future executor types
|
||||
func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNamespace string, port int) error {
|
||||
fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
|
||||
fissionClient, kubernetesClient, _, metricsClient, err := crd.MakeFissionClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get kubernetes client")
|
||||
}
|
||||
@@ -259,7 +259,7 @@ func StartExecutor(logger *zap.Logger, functionNamespace string, envBuilderNames
|
||||
|
||||
gpm := poolmgr.MakeGenericPoolManager(
|
||||
logger,
|
||||
fissionClient, kubernetesClient,
|
||||
fissionClient, kubernetesClient, metricsClient,
|
||||
functionNamespace, fetcherConfig, executorInstanceID)
|
||||
|
||||
ndm := newdeploy.MakeNewDeploy(
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestExecutor(t *testing.T) {
|
||||
|
||||
// connect to k8s
|
||||
// and get CRD client
|
||||
fissionClient, kubeClient, apiExtClient, err := crd.MakeFissionClient()
|
||||
fissionClient, kubeClient, apiExtClient, _, err := crd.MakeFissionClient()
|
||||
if err != nil {
|
||||
log.Panicf("failed to connect: %v", err)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ type ExecutorType interface {
|
||||
// GetFuncSvcFromCache retrieves function service from cache.
|
||||
GetFuncSvcFromCache(*fv1.Function) (*fscache.FuncSvc, error)
|
||||
|
||||
// GetFuncSvcFromPoolCache retrieves function service and number of active instances after filtering on requestsPerPod and CPULimit
|
||||
GetFuncSvcFromPoolCache(fn *fv1.Function, requestsPerPod int) (*fscache.FuncSvc, int, error)
|
||||
|
||||
// DeleteFuncSvcFromCache deletes function service entry in cache.
|
||||
DeleteFuncSvcFromCache(*fscache.FuncSvc)
|
||||
|
||||
@@ -60,7 +63,4 @@ type ExecutorType interface {
|
||||
|
||||
// CleanupOldExecutorObjects cleans up resources created by old executor instances
|
||||
CleanupOldExecutorObjects()
|
||||
|
||||
// getTotalAvailable returns total active instances of particular function
|
||||
GetTotalAvailable(*fv1.Function) int
|
||||
}
|
||||
|
||||
@@ -165,10 +165,10 @@ func (deploy *NewDeploy) UnTapService(key string, svcHost string) {
|
||||
// Not Implemented for NewDeployment. Will be used when support of concurrent specialization of same function is added.
|
||||
}
|
||||
|
||||
// GetTotalAvailable has not been implemented for NewDeployment.
|
||||
func (deploy *NewDeploy) GetTotalAvailable(fn *fv1.Function) int {
|
||||
// GetFuncSvcFromPoolCache has not been implemented for NewDeployment
|
||||
func (deploy *NewDeploy) GetFuncSvcFromPoolCache(fn *fv1.Function, requestsPerPod int) (*fscache.FuncSvc, int, error) {
|
||||
// Not Implemented for NewDeployment. Will be used when support of concurrent specialization of same function is added.
|
||||
return 0
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
// TapService makes a TouchByAddress request to the cache.
|
||||
|
||||
@@ -20,9 +20,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
@@ -32,13 +34,16 @@ import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
k8sErrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
k8sTypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
metricsclient "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/crd"
|
||||
@@ -63,6 +68,7 @@ type (
|
||||
useIstio bool
|
||||
runtimeImagePullPolicy apiv1.PullPolicy // pull policy for generic pool to created env deployment
|
||||
kubernetesClient *kubernetes.Clientset
|
||||
metricsClient *metricsclient.Clientset
|
||||
fissionClient *crd.FissionClient
|
||||
fetcherConfig *fetcherConfig.Config
|
||||
stopReadyPodControllerCh chan struct{}
|
||||
@@ -71,6 +77,7 @@ type (
|
||||
readyPodQueue workqueue.DelayingInterface
|
||||
poolInstanceID string // small random string to uniquify pod names
|
||||
instanceID string // poolmgr instance id
|
||||
podFSVCMap sync.Map
|
||||
}
|
||||
)
|
||||
|
||||
@@ -79,6 +86,7 @@ func MakeGenericPool(
|
||||
logger *zap.Logger,
|
||||
fissionClient *crd.FissionClient,
|
||||
kubernetesClient *kubernetes.Clientset,
|
||||
metricsClient *metricsclient.Clientset,
|
||||
env *fv1.Environment,
|
||||
initialReplicas int32,
|
||||
namespace string,
|
||||
@@ -110,6 +118,7 @@ func MakeGenericPool(
|
||||
replicas: initialReplicas, // TODO make this an env param instead?
|
||||
fissionClient: fissionClient,
|
||||
kubernetesClient: kubernetesClient,
|
||||
metricsClient: metricsClient,
|
||||
namespace: namespace,
|
||||
functionNamespace: functionNamespace,
|
||||
podReadyTimeout: podReadyTimeout,
|
||||
@@ -120,6 +129,7 @@ func MakeGenericPool(
|
||||
stopReadyPodControllerCh: make(chan struct{}),
|
||||
poolInstanceID: uniuri.NewLen(8),
|
||||
instanceID: instanceID,
|
||||
podFSVCMap: sync.Map{},
|
||||
}
|
||||
|
||||
gp.runtimeImagePullPolicy = utils.GetImagePullPolicy(os.Getenv("RUNTIME_IMAGE_PULL_POLICY"))
|
||||
@@ -141,7 +151,7 @@ func MakeGenericPool(
|
||||
gpLogger.Info("deployment created", zap.Any("environment", env.ObjectMeta))
|
||||
|
||||
go gp.startReadyPodController()
|
||||
|
||||
go gp.updateCPUUtilizationSvc()
|
||||
return gp, nil
|
||||
}
|
||||
|
||||
@@ -161,6 +171,35 @@ func (gp *GenericPool) getDeployAnnotations() map[string]string {
|
||||
}
|
||||
}
|
||||
|
||||
func (gp *GenericPool) updateCPUUtilizationSvc() {
|
||||
for {
|
||||
podMetricsList, err := gp.metricsClient.MetricsV1beta1().PodMetricses(gp.namespace).List(v1.ListOptions{
|
||||
LabelSelector: "managed=false",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
gp.logger.Error("failed to fetch pod metrics list", zap.Error(err))
|
||||
} else {
|
||||
gp.logger.Debug("pods found", zap.Any("length", len(podMetricsList.Items)))
|
||||
for _, val := range podMetricsList.Items {
|
||||
p, _ := resource.ParseQuantity("0m")
|
||||
for _, container := range val.Containers {
|
||||
p.Add(container.Usage["cpu"])
|
||||
}
|
||||
if value, ok := gp.podFSVCMap.Load(val.ObjectMeta.Name); ok {
|
||||
if valArray, ok1 := value.([]interface{}); ok1 {
|
||||
function, address := valArray[0], valArray[1]
|
||||
gp.fsCache.SetCPUUtilizaton(function.(string), address.(string), p)
|
||||
gp.logger.Info(fmt.Sprintf("updated function %s, address %s, cpuUsage %+v", function.(string), address.(string), p))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// choosePod picks a ready pod from the pool and relabels it, waiting if necessary.
|
||||
// returns the key and pod API object.
|
||||
func (gp *GenericPool) choosePod(newLabels map[string]string) (string, *apiv1.Pod, error) {
|
||||
@@ -564,7 +603,6 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
|
||||
gp.scheduleDeletePod(pod.ObjectMeta.Name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gp.logger.Info("specialized pod", zap.String("pod", pod.ObjectMeta.Name), zap.Any("function", fn.ObjectMeta))
|
||||
|
||||
var svcHost string
|
||||
@@ -623,6 +661,19 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
|
||||
UID: pod.ObjectMeta.UID,
|
||||
},
|
||||
}
|
||||
cpuUsage := resource.MustParse("0m")
|
||||
for _, container := range pod.Spec.Containers {
|
||||
val := *container.Resources.Limits.Cpu()
|
||||
cpuUsage.Add(val)
|
||||
}
|
||||
|
||||
// set cpuLimit to 85th percentage of the cpuUsage
|
||||
cpuLimit, err := gp.getPercent(cpuUsage, 0.85)
|
||||
if err != nil {
|
||||
gp.logger.Error("failed to get 85 of CPU usage", zap.Error(err))
|
||||
cpuLimit = cpuUsage
|
||||
}
|
||||
gp.logger.Debug("cpuLimit set to", zap.Any("cpulimit", cpuLimit))
|
||||
|
||||
m := fn.ObjectMeta // only cache necessary part
|
||||
fsvc := &fscache.FuncSvc{
|
||||
@@ -632,10 +683,12 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
|
||||
Address: svcHost,
|
||||
KubernetesObjects: kubeObjRefs,
|
||||
Executor: fv1.ExecutorTypePoolmgr,
|
||||
CPULimit: cpuLimit,
|
||||
Ctime: time.Now(),
|
||||
Atime: time.Now(),
|
||||
}
|
||||
|
||||
gp.podFSVCMap.Store(pod.ObjectMeta.Name, []interface{}{crd.CacheKey(fsvc.Function), fsvc.Address})
|
||||
gp.fsCache.AddFunc(*fsvc)
|
||||
|
||||
gp.fsCache.IncreaseColdStarts(fn.ObjectMeta.Name, string(fn.ObjectMeta.UID))
|
||||
@@ -643,6 +696,12 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
|
||||
return fsvc, nil
|
||||
}
|
||||
|
||||
// getPercent returns x percent of the quantity i.e multiple it x/100
|
||||
func (gp *GenericPool) getPercent(cpuUsage resource.Quantity, percentage float64) (resource.Quantity, error) {
|
||||
val := int64(math.Ceil(float64(cpuUsage.MilliValue()) * percentage))
|
||||
return resource.ParseQuantity(fmt.Sprintf("%dm", val))
|
||||
}
|
||||
|
||||
// destroys the pool -- the deployment, replicaset and pods
|
||||
func (gp *GenericPool) destroy() error {
|
||||
close(gp.stopReadyPodControllerCh)
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
k8sInformers "k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
k8sCache "k8s.io/client-go/tools/cache"
|
||||
metricsclient "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
"github.com/fission/fission/pkg/cache"
|
||||
@@ -62,6 +63,7 @@ type (
|
||||
|
||||
pools map[string]*GenericPool
|
||||
kubernetesClient *kubernetes.Clientset
|
||||
metricsClient *metricsclient.Clientset
|
||||
namespace string
|
||||
|
||||
fissionClient *crd.FissionClient
|
||||
@@ -97,6 +99,7 @@ func MakeGenericPoolManager(
|
||||
logger *zap.Logger,
|
||||
fissionClient *crd.FissionClient,
|
||||
kubernetesClient *kubernetes.Clientset,
|
||||
metricsClient *metricsclient.Clientset,
|
||||
functionNamespace string,
|
||||
fetcherConfig *fetcherConfig.Config,
|
||||
instanceID string) executortype.ExecutorType {
|
||||
@@ -107,6 +110,7 @@ func MakeGenericPoolManager(
|
||||
logger: gpmLogger,
|
||||
pools: make(map[string]*GenericPool),
|
||||
kubernetesClient: kubernetesClient,
|
||||
metricsClient: metricsClient,
|
||||
namespace: functionNamespace,
|
||||
fissionClient: fissionClient,
|
||||
functionEnv: cache.MakeCache(10*time.Second, 0),
|
||||
@@ -172,7 +176,11 @@ func (gpm *GenericPoolManager) GetFuncSvc(ctx context.Context, fn *fv1.Function)
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) GetFuncSvcFromCache(fn *fv1.Function) (*fscache.FuncSvc, error) {
|
||||
return gpm.fsCache.GetFuncSvc(&fn.ObjectMeta)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) GetFuncSvcFromPoolCache(fn *fv1.Function, requestsPerPod int) (*fscache.FuncSvc, int, error) {
|
||||
return gpm.fsCache.GetFuncSvc(&fn.ObjectMeta, requestsPerPod)
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) DeleteFuncSvcFromCache(fsvc *fscache.FuncSvc) {
|
||||
@@ -183,10 +191,6 @@ func (gpm *GenericPoolManager) UnTapService(key string, svcHost string) {
|
||||
gpm.fsCache.MarkAvailable(key, svcHost)
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) GetTotalAvailable(fn *fv1.Function) int {
|
||||
return gpm.fsCache.GetTotalAvailable(&fn.ObjectMeta)
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) TapService(svcHost string) error {
|
||||
err := gpm.fsCache.TouchByAddress(svcHost)
|
||||
if err != nil {
|
||||
@@ -457,7 +461,7 @@ func (gpm *GenericPoolManager) service() {
|
||||
}
|
||||
|
||||
pool, err = MakeGenericPool(gpm.logger,
|
||||
gpm.fissionClient, gpm.kubernetesClient, req.env, poolsize,
|
||||
gpm.fissionClient, gpm.kubernetesClient, gpm.metricsClient, req.env, poolsize,
|
||||
ns, gpm.namespace, gpm.fsCache, gpm.fetcherConfig, gpm.instanceID, gpm.enableIstio)
|
||||
if err != nil {
|
||||
req.responseChannel <- &response{error: err}
|
||||
|
||||
@@ -23,7 +23,6 @@ func (gp *GenericPool) startReadyPodController() {
|
||||
options.FieldSelector = "status.phase=Running"
|
||||
}
|
||||
readyPodWatcher := cache.NewFilteredListWatchFromClient(gp.kubernetesClient.CoreV1().RESTClient(), "pods", gp.namespace, optionsModifier)
|
||||
|
||||
gp.readyPodQueue = workqueue.NewDelayingQueue()
|
||||
gp.readyPodIndexer, gp.readyPodController = cache.NewIndexerInformer(readyPodWatcher, &apiv1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
@@ -30,7 +31,7 @@ import (
|
||||
"github.com/fission/fission/pkg/cache"
|
||||
"github.com/fission/fission/pkg/crd"
|
||||
ferror "github.com/fission/fission/pkg/error"
|
||||
poolcache "github.com/fission/fission/pkg/newcache"
|
||||
"github.com/fission/fission/pkg/poolcache"
|
||||
)
|
||||
|
||||
type fscRequestType int
|
||||
@@ -54,6 +55,7 @@ type (
|
||||
Address string // Host:Port or IP:Port that the function's service can be reached at.
|
||||
KubernetesObjects []apiv1.ObjectReference // Kubernetes Objects (within the function namespace)
|
||||
Executor fv1.ExecutorType
|
||||
CPULimit resource.Quantity
|
||||
|
||||
Ctime time.Time
|
||||
Atime time.Time
|
||||
@@ -147,8 +149,7 @@ func (fsc *FunctionServiceCache) service() {
|
||||
fscs := fsc.connFunctionCache.ListAvailableValue()
|
||||
funcObjects := make([]*FuncSvc, 0)
|
||||
for _, funcSvc := range fscs {
|
||||
fsvc := funcSvc.(*FuncSvc)
|
||||
if time.Since(fsvc.Atime) > req.age {
|
||||
if fsvc, ok := funcSvc.(*FuncSvc); ok && time.Since(fsvc.Atime) > req.age {
|
||||
funcObjects = append(funcObjects, fsvc)
|
||||
}
|
||||
}
|
||||
@@ -176,14 +177,14 @@ func (fsc *FunctionServiceCache) GetByFunction(m *metav1.ObjectMeta) (*FuncSvc,
|
||||
return &fsvcCopy, nil
|
||||
}
|
||||
|
||||
// GetFuncSvc gets a function service from pool cache using function key.
|
||||
func (fsc *FunctionServiceCache) GetFuncSvc(m *metav1.ObjectMeta) (*FuncSvc, error) {
|
||||
// GetFuncSvc gets a function service from pool cache using function key and returns number of active instances of function pod
|
||||
func (fsc *FunctionServiceCache) GetFuncSvc(m *metav1.ObjectMeta, requestsPerPod int) (*FuncSvc, int, error) {
|
||||
key := crd.CacheKey(m)
|
||||
|
||||
fsvcI, err := fsc.connFunctionCache.GetValue(key)
|
||||
fsvcI, active, err := fsc.connFunctionCache.GetValue(key, requestsPerPod)
|
||||
if err != nil {
|
||||
fsc.logger.Info("Not found in Cache")
|
||||
return nil, err
|
||||
return nil, active, err
|
||||
}
|
||||
|
||||
// update atime
|
||||
@@ -191,7 +192,7 @@ func (fsc *FunctionServiceCache) GetFuncSvc(m *metav1.ObjectMeta) (*FuncSvc, err
|
||||
fsvc.Atime = time.Now()
|
||||
|
||||
fsvcCopy := *fsvc
|
||||
return &fsvcCopy, nil
|
||||
return &fsvcCopy, active, nil
|
||||
}
|
||||
|
||||
// GetByFunctionUID gets a function service from cache using function UUID.
|
||||
@@ -218,7 +219,7 @@ func (fsc *FunctionServiceCache) GetByFunctionUID(uid types.UID) (*FuncSvc, erro
|
||||
|
||||
// AddFunc adds a function service to pool cache.
|
||||
func (fsc *FunctionServiceCache) AddFunc(fsvc FuncSvc) {
|
||||
fsc.connFunctionCache.SetValue(crd.CacheKey(fsvc.Function), fsvc.Address, &fsvc)
|
||||
fsc.connFunctionCache.SetValue(crd.CacheKey(fsvc.Function), fsvc.Address, &fsvc, fsvc.CPULimit)
|
||||
now := time.Now()
|
||||
fsvc.Ctime = now
|
||||
fsvc.Atime = now
|
||||
@@ -226,9 +227,9 @@ func (fsc *FunctionServiceCache) AddFunc(fsvc FuncSvc) {
|
||||
fsc.setFuncAlive(fsvc.Function.Name, string(fsvc.Function.UID), true)
|
||||
}
|
||||
|
||||
// GetTotalAvailable returns the total number active function services.
|
||||
func (fsc *FunctionServiceCache) GetTotalAvailable(m *metav1.ObjectMeta) int {
|
||||
return fsc.connFunctionCache.GetTotalAvailable(crd.CacheKey(m))
|
||||
// SetCPUUtilizaton updates/sets CPUutilization in the pool cache
|
||||
func (fsc *FunctionServiceCache) SetCPUUtilizaton(key string, svcHost string, cpuUsage resource.Quantity) {
|
||||
fsc.connFunctionCache.SetCPUUtilization(key, svcHost, cpuUsage)
|
||||
}
|
||||
|
||||
// MarkAvailable marks the value at key [function][address] as available.
|
||||
@@ -358,6 +359,10 @@ func (fsc *FunctionServiceCache) DeleteFunctionSvc(fsvc *FuncSvc) {
|
||||
}
|
||||
}
|
||||
|
||||
func (fsc *FunctionServiceCache) SetCPUUtilization(key string, svcHost string, cpuUsage resource.Quantity) {
|
||||
fsc.connFunctionCache.SetCPUUtilization(key, svcHost, cpuUsage)
|
||||
}
|
||||
|
||||
// DeleteOld deletes aged function service entries from cache.
|
||||
func (fsc *FunctionServiceCache) DeleteOld(fsvc *FuncSvc, minAge time.Duration) (bool, error) {
|
||||
if time.Since(fsvc.Atime) < minAge {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
@@ -171,10 +172,10 @@ func TestFunctionServiceNewCache(t *testing.T) {
|
||||
},
|
||||
Address: "xxx",
|
||||
KubernetesObjects: objects,
|
||||
CPULimit: resource.MustParse("5m"),
|
||||
Ctime: now,
|
||||
Atime: now,
|
||||
}
|
||||
|
||||
fn := &fv1.Function{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
@@ -183,8 +184,10 @@ func TestFunctionServiceNewCache(t *testing.T) {
|
||||
}
|
||||
|
||||
fsc.AddFunc(*fsvc)
|
||||
|
||||
active := fsc.GetTotalAvailable(fsvc.Function)
|
||||
_, active, err := fsc.GetFuncSvc(fsvc.Function, 5)
|
||||
if err != nil {
|
||||
logger.Panic("received error while retrieving value from cache")
|
||||
}
|
||||
if active != 1 {
|
||||
logger.Panic(fmt.Sprintln("active instances not matched expected 1, found ", active))
|
||||
}
|
||||
@@ -192,11 +195,7 @@ func TestFunctionServiceNewCache(t *testing.T) {
|
||||
key := fmt.Sprintf("%v_%v", fn.ObjectMeta.UID, fn.ObjectMeta.ResourceVersion)
|
||||
fsc.MarkAvailable(key, fsvc.Address)
|
||||
|
||||
if fsc.GetTotalAvailable(fsvc.Function) != 0 {
|
||||
log.Panicln("active instances not matched")
|
||||
}
|
||||
|
||||
_, err = fsc.GetFuncSvc(fsvc.Function)
|
||||
_, _, err = fsc.GetFuncSvc(fsvc.Function, 5)
|
||||
if err != nil {
|
||||
logger.Panic("received error while retrieving value from cache")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user