Allow to tap multiple function services at one time (#1434)

The router taps function service one by one which is inefficient and
increases the burden of executor. This PR aggregates all requests into
one to solve the problem mentioned above.
This commit is contained in:
Ta-Ching Chen
2019-11-25 18:44:48 +08:00
committed by GitHub
parent ccc551112b
commit 6d2fe08973
7 changed files with 169 additions and 91 deletions
+45 -1
View File
@@ -20,6 +20,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
@@ -32,6 +34,7 @@ import (
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1" fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error" ferror "github.com/fission/fission/pkg/error"
"github.com/fission/fission/pkg/executor/client"
) )
func (executor *Executor) getServiceForFunctionApi(w http.ResponseWriter, r *http.Request) { func (executor *Executor) getServiceForFunctionApi(w http.ResponseWriter, r *http.Request) {
@@ -115,6 +118,7 @@ func (executor *Executor) getServiceForFunction(fn *fv1.Function) (string, error
} }
// find funcSvc and update its atime // find funcSvc and update its atime
// TODO: Deprecated tapService
func (executor *Executor) tapService(w http.ResponseWriter, r *http.Request) { func (executor *Executor) tapService(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
@@ -137,6 +141,45 @@ func (executor *Executor) tapService(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
// find funcSvc and update its atime
func (executor *Executor) tapServices(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
executor.logger.Error("failed to read tap service request", zap.Error(err))
http.Error(w, "Failed to read request", http.StatusInternalServerError)
return
}
tapSvcReqs := []client.TapServiceRequest{}
err = json.Unmarshal(body, &tapSvcReqs)
if err != nil {
executor.logger.Error("failed to decode tap service request",
zap.Error(err),
zap.String("request-payload", string(body)))
http.Error(w, "Failed to decode tap service request", http.StatusBadRequest)
return
}
errs := &multierror.Error{}
for _, req := range tapSvcReqs {
svcHost := strings.TrimPrefix(req.ServiceUrl, "http://")
err = executor.fsCache.TouchByAddress(svcHost)
if err != nil {
errs = multierror.Append(errs,
errors.Wrapf(err, "'%v' failed to tap function '%v/%v' with service url '%v'",
req.FnMetadata.Namespace, req.FnMetadata.Name, req.ServiceUrl, req.FnExecutorType))
}
}
if errs.ErrorOrNil() != nil {
executor.logger.Error("error tapping function service", zap.Error(errs))
http.Error(w, "Not found", http.StatusNotFound)
return
}
w.WriteHeader(http.StatusOK)
}
func (executor *Executor) healthHandler(w http.ResponseWriter, r *http.Request) { func (executor *Executor) healthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
@@ -144,7 +187,8 @@ func (executor *Executor) healthHandler(w http.ResponseWriter, r *http.Request)
func (executor *Executor) GetHandler() http.Handler { func (executor *Executor) GetHandler() http.Handler {
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/v2/getServiceForFunction", executor.getServiceForFunctionApi).Methods("POST") r.HandleFunc("/v2/getServiceForFunction", executor.getServiceForFunctionApi).Methods("POST")
r.HandleFunc("/v2/tapService", executor.tapService).Methods("POST") r.HandleFunc("/v2/tapService", executor.tapService).Methods("POST") // for backward compatibility
r.HandleFunc("/v2/tapServices", executor.tapServices).Methods("POST")
r.HandleFunc("/healthz", executor.healthHandler).Methods("GET") r.HandleFunc("/healthz", executor.healthHandler).Methods("GET")
return r return r
} }
+61 -30
View File
@@ -26,29 +26,36 @@ import (
"strings" "strings"
"time" "time"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.opencensus.io/plugin/ochttp" "go.opencensus.io/plugin/ochttp"
"go.uber.org/zap" "go.uber.org/zap"
"golang.org/x/net/context/ctxhttp" "golang.org/x/net/context/ctxhttp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ferror "github.com/fission/fission/pkg/error"
) )
type Client struct { type (
logger *zap.Logger Client struct {
executorUrl string logger *zap.Logger
tappedByUrl map[string]bool executorUrl string
requestChan chan string tappedByUrl map[string]TapServiceRequest
httpClient *http.Client requestChan chan TapServiceRequest
} httpClient *http.Client
}
TapServiceRequest struct {
FnMetadata metav1.ObjectMeta
FnExecutorType fv1.ExecutorType
ServiceUrl string
}
)
func MakeClient(logger *zap.Logger, executorUrl string) *Client { func MakeClient(logger *zap.Logger, executorUrl string) *Client {
c := &Client{ c := &Client{
logger: logger.Named("executor_client"), logger: logger.Named("executor_client"),
executorUrl: strings.TrimSuffix(executorUrl, "/"), executorUrl: strings.TrimSuffix(executorUrl, "/"),
tappedByUrl: make(map[string]bool), tappedByUrl: make(map[string]TapServiceRequest),
requestChan: make(chan string), requestChan: make(chan TapServiceRequest),
httpClient: &http.Client{ httpClient: &http.Client{
Transport: &ochttp.Transport{}, Transport: &ochttp.Transport{},
}, },
@@ -87,40 +94,64 @@ func (c *Client) service() {
ticker := time.NewTicker(time.Second * 5) ticker := time.NewTicker(time.Second * 5)
for { for {
select { select {
case serviceUrl := <-c.requestChan: case svcReq := <-c.requestChan:
c.tappedByUrl[serviceUrl] = true c.tappedByUrl[svcReq.ServiceUrl] = svcReq
case <-ticker.C: case <-ticker.C:
urls := c.tappedByUrl if len(c.tappedByUrl) == 0 {
c.tappedByUrl = make(map[string]bool) continue
if len(urls) > 0 {
go func() {
for u := range urls {
err := c._tapService(u)
if err != nil {
c.logger.Error("error tapping function service address", zap.Error(err), zap.String("address", u))
}
}
c.logger.Debug("tapped services in batch", zap.Int("service_count", len(urls)))
}()
} }
urls := c.tappedByUrl
c.tappedByUrl = make(map[string]TapServiceRequest)
go func() {
svcReqs := []TapServiceRequest{}
for _, req := range urls {
svcReqs = append(svcReqs, req)
}
c.logger.Debug("tapped services in batch", zap.Int("service_count", len(urls)))
err := c._tapService(svcReqs)
if err != nil {
c.logger.Error("error tapping function service address", zap.Error(err))
}
}()
} }
} }
} }
func (c *Client) TapService(serviceUrl *url.URL) { func (c *Client) TapService(fnMeta metav1.ObjectMeta, executorType fv1.ExecutorType, serviceUrl *url.URL) {
c.requestChan <- serviceUrl.String() c.requestChan <- TapServiceRequest{
FnMetadata: metav1.ObjectMeta{
Name: fnMeta.Name,
Namespace: fnMeta.Namespace,
ResourceVersion: fnMeta.ResourceVersion,
UID: fnMeta.UID,
},
FnExecutorType: executorType,
// service url is for executor to know which
// pod/service is currently used to serve user function.
ServiceUrl: serviceUrl.String(),
}
} }
func (c *Client) _tapService(serviceUrlStr string) error { func (c *Client) _tapService(tapSvcReqs []TapServiceRequest) error {
executorUrl := c.executorUrl + "/v2/tapService" executorUrl := c.executorUrl + "/v2/tapServices"
resp, err := http.Post(executorUrl, "application/octet-stream", bytes.NewReader([]byte(serviceUrlStr))) body, err := json.Marshal(tapSvcReqs)
if err != nil {
return err
}
resp, err := http.Post(executorUrl, "application/json", bytes.NewReader(body))
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
return ferror.MakeErrorFromHTTP(resp) return ferror.MakeErrorFromHTTP(resp)
} }
return nil return nil
} }
+28 -28
View File
@@ -54,9 +54,9 @@ type (
logger *zap.Logger logger *zap.Logger
fmap *functionServiceMap fmap *functionServiceMap
executor *executorClient.Client executor *executorClient.Client
function *metav1.ObjectMeta function *fv1.Function
httpTrigger *fv1.HTTPTrigger httpTrigger *fv1.HTTPTrigger
functionMetadataMap map[string]*metav1.ObjectMeta functionMap map[string]*fv1.Function
fnWeightDistributionList []FunctionWeightDistribution fnWeightDistributionList []FunctionWeightDistribution
tsRoundTripperParams *tsRoundTripperParams tsRoundTripperParams *tsRoundTripperParams
isDebugEnv bool isDebugEnv bool
@@ -150,7 +150,7 @@ func (roundTripper *RetryingRoundTripper) RoundTrip(req *http.Request) (*http.Re
// Set forwarded host header if not exists // Set forwarded host header if not exists
roundTripper.addForwardedHostHeader(req) roundTripper.addForwardedHostHeader(req)
fnMeta := roundTripper.funcHandler.function fnMeta := &roundTripper.funcHandler.function.Metadata
// Metrics stuff // Metrics stuff
startTime := time.Now() startTime := time.Now()
@@ -239,7 +239,7 @@ func (roundTripper *RetryingRoundTripper) RoundTrip(req *http.Request) (*http.Re
// tapService before invoking roundTrip for the serviceUrl // tapService before invoking roundTrip for the serviceUrl
if serviceUrlFromCache { if serviceUrlFromCache {
go roundTripper.funcHandler.tapService(serviceUrl) go roundTripper.funcHandler.tapService(roundTripper.funcHandler.function, serviceUrl)
} }
// modify the request to reflect the service url // modify the request to reflect the service url
@@ -388,25 +388,25 @@ func (roundTripper *RetryingRoundTripper) closeContext() {
} }
} }
func (fh *functionHandler) tapService(serviceUrl *url.URL) { func (fh *functionHandler) tapService(fn *fv1.Function, serviceUrl *url.URL) {
if fh.executor == nil { if fh.executor == nil {
return return
} }
fh.executor.TapService(serviceUrl) fh.executor.TapService(fn.Metadata, fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType, serviceUrl)
} }
func (fh functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) { func (fh functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) {
if fh.httpTrigger != nil && fh.httpTrigger.Spec.FunctionReference.Type == types.FunctionReferenceTypeFunctionWeights { if fh.httpTrigger != nil && fh.httpTrigger.Spec.FunctionReference.Type == types.FunctionReferenceTypeFunctionWeights {
// canary deployment. need to determine the function to send request to now // canary deployment. need to determine the function to send request to now
fnMetadata := getCanaryBackend(fh.functionMetadataMap, fh.fnWeightDistributionList) fn := getCanaryBackend(fh.functionMap, fh.fnWeightDistributionList)
if fnMetadata == nil { if fn == nil {
fh.logger.Error("could not get canary backend", fh.logger.Error("could not get canary backend",
zap.Any("metadataMap", fh.functionMetadataMap), zap.Any("fnMap", fh.functionMap),
zap.Any("distributionList", fh.fnWeightDistributionList)) zap.Any("distributionList", fh.fnWeightDistributionList))
// TODO : write error to responseWrite and return response // TODO : write error to responseWrite and return response
return return
} }
fh.function = fnMetadata fh.function = fn
fh.logger.Debug("chosen function backend's metadata", zap.Any("metadata", fh.function)) fh.logger.Debug("chosen function backend's metadata", zap.Any("metadata", fh.function))
} }
@@ -414,7 +414,7 @@ func (fh functionHandler) handler(responseWriter http.ResponseWriter, request *h
setPathInfoToHeader(request) setPathInfoToHeader(request)
// system params // system params
setFunctionMetadataToHeader(fh.function, request) setFunctionMetadataToHeader(&fh.function.Metadata, request)
director := func(req *http.Request) { director := func(req *http.Request) {
if _, ok := req.Header["User-Agent"]; !ok { if _, ok := req.Header["User-Agent"]; !ok {
@@ -423,7 +423,7 @@ func (fh functionHandler) handler(responseWriter http.ResponseWriter, request *h
} }
} }
fnTimeout := fh.functionTimeoutMap[fh.function.GetUID()] fnTimeout := fh.functionTimeoutMap[fh.function.Metadata.GetUID()]
if fnTimeout == 0 { if fnTimeout == 0 {
fnTimeout = fv1.DEFAULT_FUNCTION_TIMEOUT fnTimeout = fv1.DEFAULT_FUNCTION_TIMEOUT
} }
@@ -437,7 +437,7 @@ func (fh functionHandler) handler(responseWriter http.ResponseWriter, request *h
proxy := &httputil.ReverseProxy{ proxy := &httputil.ReverseProxy{
Director: director, Director: director,
Transport: rrt, Transport: rrt,
ErrorHandler: getProxyErrorHandler(fh.logger, fh.function), ErrorHandler: getProxyErrorHandler(fh.logger, &fh.function.Metadata),
} }
defer func() { defer func() {
@@ -483,12 +483,10 @@ func findCeil(randomNumber int, wtDistrList []FunctionWeightDistribution) string
} }
// picks a function to route to based on a random number generated // picks a function to route to based on a random number generated
func getCanaryBackend(fnMetadatamap map[string]*metav1.ObjectMeta, fnWtDistributionList []FunctionWeightDistribution) *metav1.ObjectMeta { func getCanaryBackend(fnMap map[string]*fv1.Function, fnWtDistributionList []FunctionWeightDistribution) *fv1.Function {
randomNumber := rand.Intn(fnWtDistributionList[len(fnWtDistributionList)-1].sumPrefix + 1) randomNumber := rand.Intn(fnWtDistributionList[len(fnWtDistributionList)-1].sumPrefix + 1)
fnName := findCeil(randomNumber, fnWtDistributionList) fnName := findCeil(randomNumber, fnWtDistributionList)
return fnMap[fnName]
return fnMetadatamap[fnName]
} }
// getProxyErrorHandler returns a reverse proxy error handler // getProxyErrorHandler returns a reverse proxy error handler
@@ -574,28 +572,30 @@ func (fh *functionHandler) getServiceEntry() (serviceUrl *url.URL, serviceUrlFro
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
fnMeta := &fh.function.Metadata
// Use throttle to limit the total amount of requests sent // Use throttle to limit the total amount of requests sent
// to the executor to prevent it from overloaded. // to the executor to prevent it from overloaded.
recordObj, err := fh.svcAddrUpdateThrottler.RunOnce( recordObj, err := fh.svcAddrUpdateThrottler.RunOnce(
crd.CacheKey(fh.function), crd.CacheKey(fnMeta),
func(firstToTheLock bool) (interface{}, error) { func(firstToTheLock bool) (interface{}, error) {
var u *url.URL var u *url.URL
// Get service entry from executor and update cache if its the first goroutine // Get service entry from executor and update cache if its the first goroutine
if firstToTheLock { // first to the service url if firstToTheLock { // first to the service url
fh.logger.Debug("calling getServiceForFunction", fh.logger.Debug("calling getServiceForFunction",
zap.String("function_name", fh.function.Name)) zap.String("function_name", fnMeta.Name))
u, err = fh.getServiceEntryFromExecutor(ctx) u, err = fh.getServiceEntryFromExecutor(ctx)
if err != nil { if err != nil {
fh.logger.Error("error getting service url from executor", fh.logger.Error("error getting service url from executor",
zap.Error(err), zap.Error(err),
zap.String("function_name", fh.function.Name)) zap.String("function_name", fnMeta.Name))
return nil, err return nil, err
} }
// add the address in router's cache // add the address in router's cache
fh.logger.Info("assigning service url for function", fh.logger.Info("assigning service url for function",
zap.String("url", u.String()), zap.String("url", u.String()),
zap.String("function_name", fh.function.Name)) zap.String("function_name", fnMeta.Name))
fh.fmap.assign(fh.function, u) fh.fmap.assign(fnMeta, u)
} else { } else {
u, err = fh.getServiceEntryFromCache() u, err = fh.getServiceEntryFromCache()
if err != nil { if err != nil {
@@ -613,9 +613,9 @@ func (fh *functionHandler) getServiceEntry() (serviceUrl *url.URL, serviceUrlFro
e := "error updating service address entry for function" e := "error updating service address entry for function"
fh.logger.Error(e, fh.logger.Error(e,
zap.Error(err), zap.Error(err),
zap.String("function_name", fh.function.Name), zap.String("function_name", fnMeta.Name),
zap.String("function_namespace", fh.function.Namespace)) zap.String("function_namespace", fnMeta.Namespace))
return nil, false, errors.Wrapf(err, "%s %s_%s", e, fh.function.Name, fh.function.Namespace) return nil, false, errors.Wrapf(err, "%s %s_%s", e, fnMeta.Name, fnMeta.Namespace)
} }
record, ok := recordObj.(svcEntryRecord) record, ok := recordObj.(svcEntryRecord)
@@ -629,7 +629,7 @@ func (fh *functionHandler) getServiceEntry() (serviceUrl *url.URL, serviceUrlFro
// getServiceEntryFromCache returns service url entry returns from cache // getServiceEntryFromCache returns service url entry returns from cache
func (fh *functionHandler) getServiceEntryFromCache() (serviceUrl *url.URL, err error) { func (fh *functionHandler) getServiceEntryFromCache() (serviceUrl *url.URL, err error) {
// cache lookup to get serviceUrl // cache lookup to get serviceUrl
serviceUrl, err = fh.fmap.lookup(fh.function) serviceUrl, err = fh.fmap.lookup(&fh.function.Metadata)
if err != nil { if err != nil {
var errMsg string var errMsg string
@@ -642,7 +642,7 @@ func (fh *functionHandler) getServiceEntryFromCache() (serviceUrl *url.URL, err
if e.Code == ferror.ErrorNotFound { if e.Code == ferror.ErrorNotFound {
return nil, nil return nil, nil
} }
errMsg = fmt.Sprintf("Error getting function %v;s service entry from cache: %v", fh.function.Name, err) errMsg = fmt.Sprintf("Error getting function %v;s service entry from cache: %v", fh.function.Metadata.Name, err)
} }
return nil, ferror.MakeError(http.StatusInternalServerError, errMsg) return nil, ferror.MakeError(http.StatusInternalServerError, errMsg)
} }
@@ -652,7 +652,7 @@ func (fh *functionHandler) getServiceEntryFromCache() (serviceUrl *url.URL, err
// getServiceEntryFromExecutor returns service url entry returns from executor // getServiceEntryFromExecutor returns service url entry returns from executor
func (fh *functionHandler) getServiceEntryFromExecutor(ctx context.Context) (*url.URL, error) { func (fh *functionHandler) getServiceEntryFromExecutor(ctx context.Context) (*url.URL, error) {
// send a request to executor to specialize a new pod // send a request to executor to specialize a new pod
service, err := fh.executor.GetServiceForFunction(ctx, fh.function) service, err := fh.executor.GetServiceForFunction(ctx, &fh.function.Metadata)
if err != nil { if err != nil {
statusCode, errMsg := ferror.GetHTTPError(err) statusCode, errMsg := ferror.GetHTTPError(err)
fh.logger.Error("error from GetServiceForFunction", fh.logger.Error("error from GetServiceForFunction",
+7 -5
View File
@@ -57,12 +57,12 @@ func TestFunctionProxying(t *testing.T) {
backendURL := createBackendService(testResponseString) backendURL := createBackendService(testResponseString)
log.Printf("Created backend svc at %v", backendURL) log.Printf("Created backend svc at %v", backendURL)
fn := &metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault} fnMeta := metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault}
logger, err := zap.NewDevelopment() logger, err := zap.NewDevelopment()
panicIf(err) panicIf(err)
fmap := makeFunctionServiceMap(logger, 0) fmap := makeFunctionServiceMap(logger, 0)
fmap.assign(fn, backendURL) fmap.assign(&fnMeta, backendURL)
httpTrigger := &fv1.HTTPTrigger{ httpTrigger := &fv1.HTTPTrigger{
Metadata: metav1.ObjectMeta{ Metadata: metav1.ObjectMeta{
@@ -78,9 +78,11 @@ func TestFunctionProxying(t *testing.T) {
} }
fh := &functionHandler{ fh := &functionHandler{
logger: logger, logger: logger,
fmap: fmap, fmap: fmap,
function: fn, function: &fv1.Function{
Metadata: metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault},
},
tsRoundTripperParams: &tsRoundTripperParams{ tsRoundTripperParams: &tsRoundTripperParams{
timeout: 50 * time.Millisecond, timeout: 50 * time.Millisecond,
timeoutExponent: 2, timeoutExponent: 2,
+9 -9
View File
@@ -50,7 +50,7 @@ type (
// a distribution of requests across two functions. // a distribution of requests across two functions.
resolveResult struct { resolveResult struct {
resolveResultType resolveResultType
functionMetadataMap map[string]*metav1.ObjectMeta functionMap map[string]*fv1.Function
functionWtDistributionList []FunctionWeightDistribution functionWtDistributionList []FunctionWeightDistribution
} }
@@ -134,12 +134,13 @@ func (frr *functionReferenceResolver) resolveByName(namespace, name string) (*re
} }
f := obj.(*fv1.Function) f := obj.(*fv1.Function)
functionMetadataMap := make(map[string]*metav1.ObjectMeta, 1) functionMap := map[string]*fv1.Function{
functionMetadataMap[f.Metadata.Name] = &f.Metadata f.Metadata.Name: f,
}
rr := resolveResult{ rr := resolveResult{
resolveResultType: resolveResultSingleFunction, resolveResultType: resolveResultSingleFunction,
functionMetadataMap: functionMetadataMap, functionMap: functionMap,
} }
return &rr, nil return &rr, nil
@@ -147,7 +148,7 @@ func (frr *functionReferenceResolver) resolveByName(namespace, name string) (*re
func (frr *functionReferenceResolver) resolveByFunctionWeights(namespace string, fr *fv1.FunctionReference) (*resolveResult, error) { func (frr *functionReferenceResolver) resolveByFunctionWeights(namespace string, fr *fv1.FunctionReference) (*resolveResult, error) {
functionMetadataMap := make(map[string]*metav1.ObjectMeta) functionMap := make(map[string]*fv1.Function)
fnWtDistrList := make([]FunctionWeightDistribution, 0) fnWtDistrList := make([]FunctionWeightDistribution, 0)
sumPrefix := 0 sumPrefix := 0
@@ -167,19 +168,18 @@ func (frr *functionReferenceResolver) resolveByFunctionWeights(namespace string,
} }
f := obj.(*fv1.Function) f := obj.(*fv1.Function)
functionMetadataMap[f.Metadata.Name] = &f.Metadata functionMap[f.Metadata.Name] = f
sumPrefix = sumPrefix + functionWeight sumPrefix = sumPrefix + functionWeight
fnWtDistrList = append(fnWtDistrList, FunctionWeightDistribution{ fnWtDistrList = append(fnWtDistrList, FunctionWeightDistribution{
name: functionName, name: functionName,
weight: functionWeight, weight: functionWeight,
sumPrefix: sumPrefix, sumPrefix: sumPrefix,
}) })
} }
rr := resolveResult{ rr := resolveResult{
resolveResultType: resolveResultMultipleFunctions, resolveResultType: resolveResultMultipleFunctions,
functionMetadataMap: functionMetadataMap, functionMap: functionMap,
functionWtDistributionList: fnWtDistrList, functionWtDistributionList: fnWtDistrList,
} }
+10 -11
View File
@@ -142,7 +142,7 @@ func (ts *HTTPTriggerSet) getRouter(fnTimeoutMap map[types.UID]int) *mux.Router
fmap: ts.functionServiceMap, fmap: ts.functionServiceMap,
executor: ts.executor, executor: ts.executor,
httpTrigger: &trigger, httpTrigger: &trigger,
functionMetadataMap: rr.functionMetadataMap, functionMap: rr.functionMap,
fnWeightDistributionList: rr.functionWtDistributionList, fnWeightDistributionList: rr.functionWtDistributionList,
tsRoundTripperParams: ts.tsRoundTripperParams, tsRoundTripperParams: ts.tsRoundTripperParams,
isDebugEnv: ts.isDebugEnv, isDebugEnv: ts.isDebugEnv,
@@ -158,8 +158,8 @@ func (ts *HTTPTriggerSet) getRouter(fnTimeoutMap map[types.UID]int) *mux.Router
// deployment. For more details, please check "handler" function of functionHandler. // deployment. For more details, please check "handler" function of functionHandler.
if rr.resolveResultType == resolveResultSingleFunction { if rr.resolveResultType == resolveResultSingleFunction {
for _, metadata := range fh.functionMetadataMap { for _, fn := range fh.functionMap {
fh.function = metadata fh.function = fn
} }
} }
@@ -185,20 +185,19 @@ func (ts *HTTPTriggerSet) getRouter(fnTimeoutMap map[types.UID]int) *mux.Router
// Internal triggers for each function by name. Non-http // Internal triggers for each function by name. Non-http
// triggers route into these. // triggers route into these.
for _, function := range ts.functions { for i := range ts.functions {
m := function.Metadata fn := ts.functions[i]
fh := &functionHandler{ fh := &functionHandler{
logger: ts.logger.Named(m.Name), logger: ts.logger.Named(fn.Metadata.Name),
fmap: ts.functionServiceMap, fmap: ts.functionServiceMap,
function: &m, function: &fn,
executor: ts.executor, executor: ts.executor,
tsRoundTripperParams: ts.tsRoundTripperParams, tsRoundTripperParams: ts.tsRoundTripperParams,
isDebugEnv: ts.isDebugEnv, isDebugEnv: ts.isDebugEnv,
svcAddrUpdateThrottler: ts.svcAddrUpdateThrottler, svcAddrUpdateThrottler: ts.svcAddrUpdateThrottler,
functionTimeoutMap: fnTimeoutMap, functionTimeoutMap: fnTimeoutMap,
} }
muxRouter.HandleFunc(utils.UrlForFunction(function.Metadata.Name, function.Metadata.Namespace), fh.handler) muxRouter.HandleFunc(utils.UrlForFunction(fn.Metadata.Name, fn.Metadata.Namespace), fh.handler)
} }
// Healthz endpoint for the router. // Healthz endpoint for the router.
@@ -263,8 +262,8 @@ func (ts *HTTPTriggerSet) initFunctionController() (k8sCache.Store, k8sCache.Con
// update resolver function reference cache // update resolver function reference cache
for key, rr := range ts.resolver.copy() { for key, rr := range ts.resolver.copy() {
if key.namespace == fn.Metadata.Namespace && if key.namespace == fn.Metadata.Namespace &&
rr.functionMetadataMap[fn.Metadata.Name] != nil && rr.functionMap[fn.Metadata.Name] != nil &&
rr.functionMetadataMap[fn.Metadata.Name].ResourceVersion != fn.Metadata.ResourceVersion { rr.functionMap[fn.Metadata.Name].Metadata.ResourceVersion != fn.Metadata.ResourceVersion {
// invalidate resolver cache // invalidate resolver cache
ts.logger.Debug("invalidating resolver cache") ts.logger.Debug("invalidating resolver cache")
err := ts.resolver.delete(key.namespace, key.triggerName, key.triggerResourceVersion) err := ts.resolver.delete(key.namespace, key.triggerName, key.triggerResourceVersion)
+9 -7
View File
@@ -32,12 +32,12 @@ import (
func TestRouter(t *testing.T) { func TestRouter(t *testing.T) {
// metadata for a fake function // metadata for a fake function
fn := &metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault} fnMeta := metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault}
// and a reference to it // and a reference to it
fr := fv1.FunctionReference{ fr := fv1.FunctionReference{
Type: types.FunctionReferenceTypeFunctionName, Type: types.FunctionReferenceTypeFunctionName,
Name: fn.Name, Name: fnMeta.Name,
} }
// start a fake service // start a fake service
@@ -49,7 +49,7 @@ func TestRouter(t *testing.T) {
// set up the cache with this fake service // set up the cache with this fake service
fmap := makeFunctionServiceMap(logger, 0) fmap := makeFunctionServiceMap(logger, 0)
fmap.assign(fn, testServiceUrl) fmap.assign(&fnMeta, testServiceUrl)
// HTTP trigger set with a trigger for this function // HTTP trigger set with a trigger for this function
triggers, _, _ := makeHTTPTriggerSet(logger, fmap, nil, nil, nil, nil, triggers, _, _ := makeHTTPTriggerSet(logger, fmap, nil, nil, nil, nil,
@@ -81,12 +81,14 @@ func TestRouter(t *testing.T) {
triggerResourceVersion: "1234", triggerResourceVersion: "1234",
} }
fnMetaMap := make(map[string]*metav1.ObjectMeta, 1) fnMetaMap := make(map[string]*fv1.Function, 1)
fnMetaMap[fn.Name] = fn fnMetaMap[fnMeta.Name] = &fv1.Function{
Metadata: fnMeta,
}
rr := resolveResult{ rr := resolveResult{
resolveResultType: resolveResultSingleFunction, resolveResultType: resolveResultSingleFunction,
functionMetadataMap: fnMetaMap, functionMap: fnMetaMap,
} }
frr.refCache.Set(nfr, rr) frr.refCache.Set(nfr, rr)