Ability to retain specialised pods for poolmanager functions (#2830)

- added retainPods flag to take in the number of specialized pods to retain
- add retainPods in both the create function and update function command
- modify crd keys to be typed instead of string
- keep track of function generation in case of update function operation
- add delete handler function to make sure specialized pods are deleted in case function is deleted

---------

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
Signed-off-by: Pranoy Kundu <pranoy1998k@gmail.com>
Co-authored-by: Pranoy Kundu <pranoy1998k@gmail.com>
This commit is contained in:
Sanket Sudake
2023-09-27 13:33:19 +05:30
committed by GitHub
co-authored by Pranoy Kundu
parent 657aee7cc2
commit 56b49dcee8
26 changed files with 405 additions and 211 deletions
+67 -17
View File
@@ -24,7 +24,9 @@ import (
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types"
"github.com/fission/fission/pkg/crd"
ferror "github.com/fission/fission/pkg/error"
otelUtils "github.com/fission/fission/pkg/utils/otel"
)
@@ -40,6 +42,7 @@ const (
setCPUUtilization
markSpecializationFailure
logFuncSvc
markDeleted
)
type (
@@ -52,14 +55,16 @@ type (
funcSvcGroup struct {
svcWaiting int
svcRetain int
svcs map[string]*funcSvcInfo
queue *Queue
deleted bool
}
// PoolCache implements a simple cache implementation having values mapped by two keys [function][address].
// As of now PoolCache is only used by poolmanager executor
PoolCache struct {
cache map[string]*funcSvcGroup
cache map[crd.CacheKeyURG]*funcSvcGroup
requestChannel chan *request
logger *zap.Logger
}
@@ -67,7 +72,7 @@ type (
request struct {
requestType
ctx context.Context
function string
function crd.CacheKeyURG
address string
dumpWriter io.Writer
value *FuncSvc
@@ -75,6 +80,7 @@ type (
cpuUsage resource.Quantity
responseChannel chan *response
concurrency int
svcsRetain int
}
response struct {
error
@@ -92,7 +98,7 @@ type (
func NewPoolCache(logger *zap.Logger) *PoolCache {
c := &PoolCache{
cache: make(map[string]*funcSvcGroup),
cache: make(map[crd.CacheKeyURG]*funcSvcGroup),
requestChannel: make(chan *request),
logger: logger,
}
@@ -131,7 +137,7 @@ func (c *PoolCache) service() {
// mark active
funcSvcGroup.svcs[addr].activeRequests++
if c.logger.Core().Enabled(zap.DebugLevel) {
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Increase active requests with getValue", zap.String("function", req.function), zap.String("address", addr), zap.Int("activeRequests", funcSvcGroup.svcs[addr].activeRequests))
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Increase active requests with getValue", zap.String("function", req.function.String()), zap.String("address", addr), zap.Int("activeRequests", funcSvcGroup.svcs[addr].activeRequests))
}
resp.value = funcSvcGroup.svcs[addr].val
found = true
@@ -172,6 +178,7 @@ func (c *PoolCache) service() {
if _, ok := c.cache[req.function].svcs[req.address]; !ok {
c.cache[req.function].svcs[req.address] = &funcSvcInfo{}
}
c.cache[req.function].svcRetain = req.svcsRetain
c.cache[req.function].svcs[req.address].val = req.value
c.cache[req.function].svcs[req.address].activeRequests++
if c.cache[req.function].svcWaiting > 0 {
@@ -196,22 +203,52 @@ func (c *PoolCache) service() {
}
}
if c.logger.Core().Enabled(zap.DebugLevel) {
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Increase active requests with setValue", zap.String("function", req.function), zap.String("address", req.address), zap.Int("activeRequests", c.cache[req.function].svcs[req.address].activeRequests))
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Increase active requests with setValue", zap.String("function", req.function.String()), zap.String("address", req.address), zap.Int("activeRequests", c.cache[req.function].svcs[req.address].activeRequests))
}
c.cache[req.function].svcs[req.address].cpuLimit = req.cpuUsage
case markDeleted:
for key := range c.cache {
if key.UID == req.function.UID {
c.cache[key].deleted = true
break
}
}
case listAvailableValue:
vals := make([]*FuncSvc, 0)
latestFuncGen := make(map[types.UID]int64)
// find the latest generation of each function
for key := range c.cache {
if currentFuncGen, ok := latestFuncGen[key.UID]; ok {
if key.Generation > currentFuncGen {
latestFuncGen[key.UID] = key.Generation
}
} else {
latestFuncGen[key.UID] = key.Generation
}
}
for key1, values := range c.cache {
svcRetain := values.svcRetain
// if the function is not latest generation, then we don't need to retain any pods
if latestFuncGen[key1.UID] != key1.Generation || values.deleted {
svcRetain = 0
}
svcCleanQuota := len(values.svcs) - svcRetain
if svcCleanQuota <= 0 {
continue
}
for key2, value := range values.svcs {
debugLevel := c.logger.Core().Enabled(zap.DebugLevel)
if debugLevel {
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Reading active requests", zap.String("function", key1), zap.String("address", key2), zap.Int("activeRequests", value.activeRequests))
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Reading active requests", zap.String("function", key1.String()), zap.String("address", key2), zap.Int("activeRequests", value.activeRequests))
}
if value.activeRequests == 0 {
if value.activeRequests == 0 && svcCleanQuota > 0 {
if debugLevel {
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Function service with no active requests", zap.String("function", key1), zap.String("address", key2), zap.Int("activeRequests", value.activeRequests))
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Function service with no active requests", zap.String("function", key1.String()), zap.String("address", key2), zap.Int("activeRequests", value.activeRequests))
}
vals = append(vals, value.val)
svcCleanQuota--
}
}
}
@@ -230,10 +267,10 @@ func (c *PoolCache) service() {
if c.cache[req.function].svcs[req.address].activeRequests > 0 {
c.cache[req.function].svcs[req.address].activeRequests--
if c.logger.Core().Enabled(zap.DebugLevel) {
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Decrease active requests", zap.String("function", req.function), zap.String("address", req.address), zap.Int("activeRequests", c.cache[req.function].svcs[req.address].activeRequests))
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Debug("Decrease active requests", zap.String("function", req.function.String()), zap.String("address", req.address), zap.Int("activeRequests", c.cache[req.function].svcs[req.address].activeRequests))
}
} else {
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Error("Invalid request to decrease active requests", zap.String("function", req.function), zap.String("address", req.address), zap.Int("activeRequests", c.cache[req.function].svcs[req.address].activeRequests))
otelUtils.LoggerWithTraceID(req.ctx, c.logger).Error("Invalid request to decrease active requests", zap.String("function", req.function.String()), zap.String("address", req.address), zap.Int("activeRequests", c.cache[req.function].svcs[req.address].activeRequests))
}
}
}
@@ -246,7 +283,12 @@ func (c *PoolCache) service() {
}
}
case deleteValue:
delete(c.cache[req.function].svcs, req.address)
if funcSvcGroup, ok := c.cache[req.function]; ok {
delete(c.cache[req.function].svcs, req.address)
if funcSvcGroup.deleted && len(c.cache[req.function].svcs) == 0 {
delete(c.cache, req.function)
}
}
req.responseChannel <- resp
case logFuncSvc:
datawriter := bufio.NewWriter(req.dumpWriter)
@@ -298,8 +340,15 @@ func (c *PoolCache) service() {
}
}
func (c *PoolCache) MarkFuncDeleted(function crd.CacheKeyURG) {
c.requestChannel <- &request{
requestType: markDeleted,
function: function,
}
}
// GetValue returns a function service with status in Active else return error
func (c *PoolCache) GetSvcValue(ctx context.Context, function string, requestsPerPod int, concurrency int) (*FuncSvc, error) {
func (c *PoolCache) GetSvcValue(ctx context.Context, function crd.CacheKeyURG, requestsPerPod int, concurrency int) (*FuncSvc, error) {
respChannel := make(chan *response)
c.requestChannel <- &request{
ctx: ctx,
@@ -334,7 +383,7 @@ func (c *PoolCache) ListAvailableValue() []*FuncSvc {
}
// SetValue marks the value at key [function][address] as active(begin used)
func (c *PoolCache) SetSvcValue(ctx context.Context, function, address string, value *FuncSvc, cpuLimit resource.Quantity, requestsPerPod int) {
func (c *PoolCache) SetSvcValue(ctx context.Context, function crd.CacheKeyURG, address string, value *FuncSvc, cpuLimit resource.Quantity, requestsPerPod, svcsRetain int) {
respChannel := make(chan *response)
c.requestChannel <- &request{
ctx: ctx,
@@ -344,12 +393,13 @@ func (c *PoolCache) SetSvcValue(ctx context.Context, function, address string, v
value: value,
cpuUsage: cpuLimit,
requestsPerPod: requestsPerPod,
svcsRetain: svcsRetain,
responseChannel: respChannel,
}
}
// SetCPUUtilization updates/sets the CPU utilization limit for the pod
func (c *PoolCache) SetCPUUtilization(function, address string, cpuUsage resource.Quantity) {
func (c *PoolCache) SetCPUUtilization(function crd.CacheKeyURG, address string, cpuUsage resource.Quantity) {
c.requestChannel <- &request{
requestType: setCPUUtilization,
function: function,
@@ -360,7 +410,7 @@ func (c *PoolCache) SetCPUUtilization(function, address string, cpuUsage resourc
}
// MarkAvailable marks the value at key [function][address] as available
func (c *PoolCache) MarkAvailable(function, address string) {
func (c *PoolCache) MarkAvailable(function crd.CacheKeyURG, address string) {
respChannel := make(chan *response)
c.requestChannel <- &request{
requestType: markAvailable,
@@ -371,7 +421,7 @@ func (c *PoolCache) MarkAvailable(function, address string) {
}
// DeleteValue deletes the value at key composed of [function][address]
func (c *PoolCache) DeleteValue(ctx context.Context, function, address string) error {
func (c *PoolCache) DeleteValue(ctx context.Context, function crd.CacheKeyURG, address string) error {
respChannel := make(chan *response)
c.requestChannel <- &request{
ctx: ctx,
@@ -385,7 +435,7 @@ func (c *PoolCache) DeleteValue(ctx context.Context, function, address string) e
}
// ReduceSpecializationInProgress reduces the svcWaiting count
func (c *PoolCache) MarkSpecializationFailure(function string) {
func (c *PoolCache) MarkSpecializationFailure(function crd.CacheKeyURG) {
c.requestChannel <- &request{
requestType: markSpecializationFailure,
function: function,