Skip to content

Commit

Permalink
chore: P10K optimizations (#8045)
Browse files Browse the repository at this point in the history
  • Loading branch information
free6om committed Sep 2, 2024
1 parent 5fe4b49 commit 80d091a
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 19 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.6.0
github.com/imdario/mergo v0.3.14
github.com/json-iterator/go v1.1.12
github.com/klauspost/compress v1.17.8
github.com/kubernetes-csi/external-snapshotter/client/v3 v3.0.0
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.2.0
Expand Down Expand Up @@ -154,7 +155,6 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/kubernetes-csi/external-snapshotter/client/v7 v7.0.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
Expand Down
25 changes: 17 additions & 8 deletions pkg/controller/instanceset/instance_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"slices"
"sort"
"strconv"
Expand Down Expand Up @@ -66,8 +65,6 @@ type instanceSetExt struct {
instanceTemplates []*workloads.InstanceTemplate
}

var instanceNameRegex = regexp.MustCompile("(.*)-([0-9]+)$")

var (
reader *zstd.Decoder
writer *zstd.Encoder
Expand All @@ -91,13 +88,15 @@ type instance struct {
func ParseParentNameAndOrdinal(s string) (string, int) {
parent := s
ordinal := -1
subMatches := instanceNameRegex.FindStringSubmatch(s)
if len(subMatches) < 3 {

index := strings.LastIndex(s, "-")
if index < 0 {
return parent, ordinal
}
parent = subMatches[1]
if i, err := strconv.ParseInt(subMatches[2], 10, 32); err == nil {
ordinalStr := s[index+1:]
if i, err := strconv.ParseInt(ordinalStr, 10, 32); err == nil {
ordinal = int(i)
parent = s[:index]
}
return parent, ordinal
}
Expand All @@ -110,8 +109,18 @@ func sortObjects[T client.Object](objects []T, rolePriorityMap map[string]int, r
role := strings.ToLower(objects[i].GetLabels()[constant.RoleLabelKey])
return rolePriorityMap[role]
}

// cache the parent names and ordinals to accelerate the parsing process when there is a massive number of Pods.
namesCache := make(map[string]string, len(objects))
ordinalsCache := make(map[string]int, len(objects))
getNameNOrdinalFunc := func(i int) (string, int) {
return ParseParentNameAndOrdinal(objects[i].GetName())
if name, ok := namesCache[objects[i].GetName()]; ok {
return name, ordinalsCache[objects[i].GetName()]
}
name, ordinal := ParseParentNameAndOrdinal(objects[i].GetName())
namesCache[objects[i].GetName()] = name
ordinalsCache[objects[i].GetName()] = ordinal
return name, ordinal
}
baseSort(objects, getNameNOrdinalFunc, getRolePriorityFunc, reverse)
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/controller/instanceset/instance_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ package instanceset
import (
"fmt"
"strings"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gmeasure"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
Expand Down Expand Up @@ -1341,4 +1343,21 @@ var _ = Describe("instance util test", func() {
Expect(affinity2).Should(Equal(expectMergedAffinity))
})
})

Context("ParseParentNameAndOrdinal", func() {
It("Benchmark", Serial, Label("measurement"), func() {
experiment := gmeasure.NewExperiment("ParseParentNameAndOrdinal Benchmark")
AddReportEntry(experiment.Name, experiment)

experiment.Sample(func(idx int) {
experiment.MeasureDuration("ParseParentNameAndOrdinal", func() {
_, _ = ParseParentNameAndOrdinal("foo-bar-666")
})
}, gmeasure.SamplingConfig{N: 100, Duration: time.Second})

parsingStats := experiment.GetStats("ParseParentNameAndOrdinal")
medianDuration := parsingStats.DurationFor(gmeasure.StatMedian)
Expect(medianDuration).To(BeNumerically("<", time.Millisecond))
})
})
})
18 changes: 10 additions & 8 deletions pkg/controller/instanceset/reconciler_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,17 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
}
unavailable := maxUnavailable - currentUnavailable

// TODO(free6om): compute updateCount from PodManagementPolicy(Serial/OrderedReady, Parallel, BestEffortParallel).
// align MemberUpdateStrategy with PodManagementPolicy if it has nil value.
itsForPlan := getInstanceSetForUpdatePlan(its)
plan := NewUpdatePlan(*itsForPlan, oldPodList, IsPodUpdated)
podsToBeUpdated, err := plan.Execute()
if err != nil {
return kubebuilderx.Continue, err
// if it's a roleful InstanceSet, we use updateCount to represent Pods can be updated according to the spec.memberUpdateStrategy.
updateCount := len(oldPodList)
if len(its.Spec.Roles) > 0 {
itsForPlan := getInstanceSetForUpdatePlan(its)
plan := NewUpdatePlan(*itsForPlan, oldPodList, IsPodUpdated)
podsToBeUpdated, err := plan.Execute()
if err != nil {
return kubebuilderx.Continue, err
}
updateCount = len(podsToBeUpdated)
}
updateCount := len(podsToBeUpdated)

updatingPods := 0
updatedPods := 0
Expand Down
15 changes: 13 additions & 2 deletions pkg/controller/instanceset/revision_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"hash/fnv"
"strconv"

jsoniter "github.com/json-iterator/go"
apps "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -37,6 +38,7 @@ import (

workloads "github.com/apecloud/kubeblocks/apis/workloads/v1alpha1"
"github.com/apecloud/kubeblocks/pkg/controller/model"
"github.com/apecloud/kubeblocks/pkg/lru"
viper "github.com/apecloud/kubeblocks/pkg/viperx"
)

Expand All @@ -47,6 +49,8 @@ var Codecs = serializer.NewCodecFactory(model.GetScheme())
var patchCodec = Codecs.LegacyCodec(workloads.SchemeGroupVersion)
var controllerKind = apps.SchemeGroupVersion.WithKind("StatefulSet")

var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary

func NewRevision(its *workloads.InstanceSet) (*apps.ControllerRevision, error) {
patch, err := getPatch(its)
if err != nil {
Expand Down Expand Up @@ -159,6 +163,8 @@ func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) {
fmt.Fprintf(hasher, "%v", dump.ForHash(objectToWrite))
}

var revisionsCache = lru.New(1024)

func GetRevisions(revisions map[string]string) (map[string]string, error) {
if revisions == nil {
return nil, nil
Expand All @@ -167,6 +173,9 @@ func GetRevisions(revisions map[string]string) (map[string]string, error) {
if !ok {
return revisions, nil
}
if revisionsInCache, ok := revisionsCache.Get(revisionsStr); ok {
return revisionsInCache.(map[string]string), nil
}
revisionsData, err := base64.StdEncoding.DecodeString(revisionsStr)
if err != nil {
return nil, err
Expand All @@ -176,9 +185,11 @@ func GetRevisions(revisions map[string]string) (map[string]string, error) {
return nil, err
}
updateRevisions := make(map[string]string)
if err = json.Unmarshal(revisionsJSON, &updateRevisions); err != nil {

if err = jsonIter.Unmarshal(revisionsJSON, &updateRevisions); err != nil {
return nil, err
}
revisionsCache.Put(revisionsStr, updateRevisions)
return updateRevisions, nil
}

Expand All @@ -187,7 +198,7 @@ func buildRevisions(updateRevisions map[string]string) (map[string]string, error
if len(updateRevisions) <= maxPlainRevisionCount {
return updateRevisions, nil
}
revisionsJSON, err := json.Marshal(updateRevisions)
revisionsJSON, err := jsonIter.Marshal(updateRevisions)
if err != nil {
return nil, err
}
Expand Down
76 changes: 76 additions & 0 deletions pkg/lru/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright (C) 2022-2024 ApeCloud Co., Ltd
This file is part of KubeBlocks project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package lru

import (
"container/list"
"sync"
)

type Cache struct {
capacity int
m sync.RWMutex
list *list.List
items map[string]*list.Element
}

type cacheItem struct {
key string
value any
}

func New(capacity int) *Cache {
return &Cache{
capacity: capacity,
list: list.New(),
items: make(map[string]*list.Element, capacity),
}
}

func (c *Cache) Get(key string) (any, bool) {
c.m.RLock()
defer c.m.RUnlock()

if elem, ok := c.items[key]; ok {
c.list.MoveToFront(elem)
return elem.Value.(*cacheItem).value, true
}
return nil, false
}

func (c *Cache) Put(key string, value any) {
c.m.Lock()
defer c.m.Unlock()

if elem, ok := c.items[key]; ok {
c.list.MoveToFront(elem)
elem.Value.(*cacheItem).value = value
return
}

if c.list.Len() == c.capacity {
last := c.list.Back()
c.list.Remove(last)
delete(c.items, last.Value.(*cacheItem).key)
}

elem := c.list.PushFront(&cacheItem{key, value})
c.items[key] = elem
}

0 comments on commit 80d091a

Please sign in to comment.