Skip to content

Commit fe04a6f

Browse files
Merge pull request #16398 from smarterclayton/disablecache
Automatic merge from submit-queue (batch tested with PRs 16546, 16398, 16157) Backport upstream changes to watch cache enablement Disables the watch cache for most resources by default, except those accessed by many clients. This has been shown to have minor impacts on the production workload. Fixes #16112
2 parents 9fe4db8 + 01aeb23 commit fe04a6f

File tree

55 files changed

+179
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+179
-272
lines changed

contrib/completions/bash/openshift

+2
Original file line numberDiff line numberDiff line change
@@ -32808,6 +32808,8 @@ _openshift_start_kubernetes_apiserver()
3280832808
local_nonpersistent_flags+=("--contention-profiling")
3280932809
flags+=("--cors-allowed-origins=")
3281032810
local_nonpersistent_flags+=("--cors-allowed-origins=")
32811+
flags+=("--default-watch-cache-size=")
32812+
local_nonpersistent_flags+=("--default-watch-cache-size=")
3281132813
flags+=("--delete-collection-workers=")
3281232814
local_nonpersistent_flags+=("--delete-collection-workers=")
3281332815
flags+=("--deserialization-cache-size=")

contrib/completions/zsh/openshift

+2
Original file line numberDiff line numberDiff line change
@@ -32957,6 +32957,8 @@ _openshift_start_kubernetes_apiserver()
3295732957
local_nonpersistent_flags+=("--contention-profiling")
3295832958
flags+=("--cors-allowed-origins=")
3295932959
local_nonpersistent_flags+=("--cors-allowed-origins=")
32960+
flags+=("--default-watch-cache-size=")
32961+
local_nonpersistent_flags+=("--default-watch-cache-size=")
3296032962
flags+=("--delete-collection-workers=")
3296132963
local_nonpersistent_flags+=("--delete-collection-workers=")
3296232964
flags+=("--deserialization-cache-size=")

pkg/cmd/server/kubernetes/master/master_config.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ import (
7777
"github.com/openshift/origin/pkg/version"
7878
)
7979

80-
const DefaultWatchCacheSize = 1000
81-
8280
// request paths that match this regular expression will be treated as long running
8381
// and not subjected to the default server timeout.
8482
const originLongRunningEndpointsRE = "(/|^)(buildconfigs/.*/instantiatebinary|imagestreamimports)$"
@@ -147,7 +145,7 @@ func BuildKubeAPIserverOptions(masterConfig configapi.MasterConfig) (*kapiserver
147145
server.Etcd.StorageConfig.KeyFile = masterConfig.EtcdClientInfo.ClientCert.KeyFile
148146
server.Etcd.StorageConfig.CertFile = masterConfig.EtcdClientInfo.ClientCert.CertFile
149147
server.Etcd.StorageConfig.CAFile = masterConfig.EtcdClientInfo.CA
150-
server.Etcd.DefaultWatchCacheSize = DefaultWatchCacheSize
148+
server.Etcd.DefaultWatchCacheSize = 0
151149

152150
server.GenericServerRunOptions.CorsAllowedOriginList = masterConfig.CORSAllowedOrigins
153151
server.GenericServerRunOptions.MaxRequestsInFlight = masterConfig.ServingInfo.MaxRequestsInFlight
@@ -438,6 +436,20 @@ func buildKubeApiserverConfig(
438436
return originLongRunningRequestRE.MatchString(r.URL.Path) || kubeLongRunningFunc(r, requestInfo)
439437
}
440438

439+
if apiserverOptions.Etcd.EnableWatchCache {
440+
glog.V(2).Infof("Initializing cache sizes based on %dMB limit", apiserverOptions.GenericServerRunOptions.TargetRAMMB)
441+
sizes := cachesize.NewHeuristicWatchCacheSizes(apiserverOptions.GenericServerRunOptions.TargetRAMMB)
442+
if userSpecified, err := genericoptions.ParseWatchCacheSizes(apiserverOptions.Etcd.WatchCacheSizes); err == nil {
443+
for resource, size := range userSpecified {
444+
sizes[resource] = size
445+
}
446+
}
447+
apiserverOptions.Etcd.WatchCacheSizes, err = genericoptions.WriteWatchCacheSizes(sizes)
448+
if err != nil {
449+
return nil, err
450+
}
451+
}
452+
441453
if err := apiserverOptions.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig); err != nil {
442454
return nil, err
443455
}
@@ -523,12 +535,6 @@ func buildKubeApiserverConfig(
523535
EnableCoreControllers: true,
524536
}
525537

526-
if apiserverOptions.Etcd.EnableWatchCache {
527-
// TODO(rebase): upstream also does the following:
528-
// cachesize.InitializeWatchCacheSizes(s.GenericServerRunOptions.TargetRAMMB)
529-
cachesize.SetWatchCacheSizes(apiserverOptions.GenericServerRunOptions.WatchCacheSizes)
530-
}
531-
532538
if kubeApiserverConfig.EnableCoreControllers {
533539
ttl := masterConfig.KubernetesMasterConfig.MasterEndpointReconcileTTL
534540
interval := ttl * 2 / 3

pkg/cmd/server/kubernetes/master/master_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ func TestNewMasterLeasesHasCorrectTTL(t *testing.T) {
2525
}
2626

2727
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1}
28-
watchCacheDisabled := 0
29-
storageInterface, _ := restOptions.Decorator(kapi.Scheme, restOptions.StorageConfig, &watchCacheDisabled, nil, "masterleases", nil, nil, nil, nil)
28+
storageInterface, _ := restOptions.Decorator(kapi.Scheme, restOptions.StorageConfig, nil, "masterleases", nil, nil, nil, nil)
3029
defer server.Terminate(t)
3130

3231
masterLeases := newMasterLeases(storageInterface, 15)

pkg/security/registry/securitycontextconstraints/etcd/etcd.go

-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"k8s.io/apiserver/pkg/registry/generic/registry"
77
"k8s.io/apiserver/pkg/registry/rest"
88
"k8s.io/kubernetes/pkg/api"
9-
"k8s.io/kubernetes/pkg/registry/cachesize"
109

1110
securityapi "github.com/openshift/origin/pkg/security/apis/security"
1211
"github.com/openshift/origin/pkg/security/registry/securitycontextconstraints"
@@ -30,7 +29,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
3029
},
3130
PredicateFunc: securitycontextconstraints.Matcher,
3231
DefaultQualifiedResource: securityapi.Resource("securitycontextconstraints"),
33-
WatchCacheSize: cachesize.GetWatchCacheSizeByResource("securitycontextconstraints"),
3432

3533
CreateStrategy: securitycontextconstraints.Strategy,
3634
UpdateStrategy: securitycontextconstraints.Strategy,

pkg/util/restoptions/configgetter.go

+43-58
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
package restoptions
22

33
import (
4-
"fmt"
5-
"strconv"
6-
"strings"
74
"sync"
85

9-
"k8s.io/apimachinery/pkg/runtime"
6+
"github.com/golang/glog"
7+
108
"k8s.io/apimachinery/pkg/runtime/schema"
11-
kerrors "k8s.io/apimachinery/pkg/util/errors"
129
"k8s.io/apiserver/pkg/registry/generic"
1310
"k8s.io/apiserver/pkg/registry/generic/registry"
11+
"k8s.io/apiserver/pkg/server/options"
1412
serverstorage "k8s.io/apiserver/pkg/server/storage"
15-
"k8s.io/apiserver/pkg/storage"
16-
"k8s.io/apiserver/pkg/storage/storagebackend"
17-
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
1813

19-
"github.com/golang/glog"
2014
configapi "github.com/openshift/origin/pkg/cmd/server/api"
2115
kubernetes "github.com/openshift/origin/pkg/cmd/server/kubernetes/master"
2216
)
@@ -41,7 +35,6 @@ type configRESTOptionsGetter struct {
4135
}
4236

4337
// NewConfigGetter returns a restoptions.Getter implemented using information from the provided master config.
44-
// By default, the etcd watch cache is enabled with a size of 1000 per resource type.
4538
// TODO: this class should either not need to know about configapi.MasterConfig, or not be in pkg/util
4639
func NewConfigGetter(masterOptions configapi.MasterConfig, defaultResourceConfig *serverstorage.ResourceConfig, resourcePrefixOverrides map[schema.GroupResource]string, enforcedStorageVersions map[schema.GroupResource]schema.GroupVersion, quorumResources map[schema.GroupResource]struct{}) (Getter, error) {
4740
apiserverOptions, err := kubernetes.BuildKubeAPIserverOptions(masterOptions)
@@ -55,27 +48,24 @@ func NewConfigGetter(masterOptions configapi.MasterConfig, defaultResourceConfig
5548
storageFactory.DefaultResourcePrefixes = resourcePrefixOverrides
5649
storageFactory.StorageConfig.Prefix = masterOptions.EtcdStorageConfig.OpenShiftStoragePrefix
5750

58-
// TODO: refactor vendor/k8s.io/kubernetes/pkg/registry/cachesize to remove our custom cache size code
59-
errs := []error{}
60-
cacheSizes := map[schema.GroupResource]int{}
61-
for _, c := range apiserverOptions.GenericServerRunOptions.WatchCacheSizes {
62-
tokens := strings.Split(c, "#")
63-
if len(tokens) != 2 {
64-
errs = append(errs, fmt.Errorf("invalid watch cache size value '%s', expecting <resource>#<size> format (e.g. builds#100)", c))
65-
continue
51+
// perform watch cache heuristic like upstream
52+
if apiserverOptions.Etcd.EnableWatchCache {
53+
glog.V(2).Infof("Initializing cache sizes based on %dMB limit", apiserverOptions.GenericServerRunOptions.TargetRAMMB)
54+
sizes := newHeuristicWatchCacheSizes(apiserverOptions.GenericServerRunOptions.TargetRAMMB)
55+
if userSpecified, err := options.ParseWatchCacheSizes(apiserverOptions.Etcd.WatchCacheSizes); err == nil {
56+
for resource, size := range userSpecified {
57+
sizes[resource] = size
58+
}
6659
}
67-
68-
resource := schema.ParseGroupResource(tokens[0])
69-
70-
size, err := strconv.Atoi(tokens[1])
60+
apiserverOptions.Etcd.WatchCacheSizes, err = options.WriteWatchCacheSizes(sizes)
7161
if err != nil {
72-
errs = append(errs, fmt.Errorf("invalid watch cache size value '%s': %v", c, err))
73-
continue
62+
return nil, err
7463
}
75-
cacheSizes[resource] = size
7664
}
77-
if len(errs) > 0 {
78-
return nil, kerrors.NewAggregate(errs)
65+
66+
cacheSizes, err := options.ParseWatchCacheSizes(apiserverOptions.Etcd.WatchCacheSizes)
67+
if err != nil {
68+
return nil, err
7969
}
8070

8171
return &configRESTOptionsGetter{
@@ -108,41 +98,14 @@ func (g *configRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource)
10898
config.Quorum = true
10999
}
110100

111-
configuredCacheSize, specified := g.cacheSizes[resource]
112-
if !specified || configuredCacheSize < 0 {
113-
configuredCacheSize = g.defaultCacheSize
114-
}
115-
storageWithCacher := registry.StorageWithCacher(configuredCacheSize)
116-
117-
decorator := func(
118-
copier runtime.ObjectCopier,
119-
storageConfig *storagebackend.Config,
120-
requestedSize *int,
121-
objectType runtime.Object,
122-
resourcePrefix string,
123-
keyFunc func(obj runtime.Object) (string, error),
124-
newListFn func() runtime.Object,
125-
getAttrsFunc storage.AttrFunc,
126-
triggerFn storage.TriggerPublisherFunc,
127-
) (storage.Interface, factory.DestroyFunc) {
128-
// use the origin default cache size, not the one in registry.StorageWithCacher
129-
capacity := &configuredCacheSize
130-
if requestedSize != nil {
131-
capacity = requestedSize
132-
}
133-
134-
if *capacity == 0 || !g.cacheEnabled {
135-
glog.V(5).Infof("using uncached watch storage for %s (quorum=%t)", resource.String(), storageConfig.Quorum)
136-
return generic.UndecoratedStorage(copier, storageConfig, capacity, objectType, resourcePrefix, keyFunc, newListFn, getAttrsFunc, triggerFn)
137-
}
138-
139-
glog.V(5).Infof("using watch cache storage (capacity=%v, quorum=%t) for %s %#v", *capacity, storageConfig.Quorum, resource.String(), storageConfig)
140-
return storageWithCacher(copier, storageConfig, capacity, objectType, resourcePrefix, keyFunc, newListFn, getAttrsFunc, triggerFn)
101+
cacheSize, ok := g.cacheSizes[resource]
102+
if !ok {
103+
cacheSize = g.defaultCacheSize
141104
}
142105

143106
resourceOptions := generic.RESTOptions{
144107
StorageConfig: config,
145-
Decorator: decorator,
108+
Decorator: registry.StorageWithCacher(cacheSize),
146109
DeleteCollectionWorkers: g.deleteCollectionWorkers,
147110
EnableGarbageCollection: g.enableGarbageCollection,
148111
ResourcePrefix: g.storageFactory.ResourcePrefix(resource),
@@ -151,3 +114,25 @@ func (g *configRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource)
151114

152115
return resourceOptions, nil
153116
}
117+
118+
// newHeuristicWatchCacheSizes returns a map of suggested watch cache sizes based on total
119+
// memory. It reuses the upstream heuristic and adds OpenShift specific resources.
120+
func newHeuristicWatchCacheSizes(expectedRAMCapacityMB int) map[schema.GroupResource]int {
121+
// TODO: Revisit this heuristic, copied from upstream
122+
clusterSize := expectedRAMCapacityMB / 60
123+
124+
// default enable watch caches for resources that will have a high number of clients accessing it
125+
// and where the write rate may be significant
126+
watchCacheSizes := make(map[schema.GroupResource]int)
127+
watchCacheSizes[schema.GroupResource{Group: "network.openshift.io", Resource: "hostsubnets"}] = maxInt(5*clusterSize, 100)
128+
watchCacheSizes[schema.GroupResource{Group: "network.openshift.io", Resource: "netnamespaces"}] = maxInt(5*clusterSize, 100)
129+
watchCacheSizes[schema.GroupResource{Group: "network.openshift.io", Resource: "egressnetworkpolicies"}] = maxInt(10*clusterSize, 100)
130+
return watchCacheSizes
131+
}
132+
133+
func maxInt(a, b int) int {
134+
if a > b {
135+
return a
136+
}
137+
return b
138+
}

test/integration/watch_cache_test.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
1616

1717
configapi "github.com/openshift/origin/pkg/cmd/server/api"
18-
serverkube "github.com/openshift/origin/pkg/cmd/server/kubernetes/master"
1918
testutil "github.com/openshift/origin/test/util"
2019
testserver "github.com/openshift/origin/test/util/server"
2120
)
@@ -124,9 +123,13 @@ func TestDefaultWatchCacheSize(t *testing.T) {
124123
etcdOptions := apiserveroptions.NewEtcdOptions(&storagebackend.Config{})
125124
kubeDefaultCacheSize := etcdOptions.DefaultWatchCacheSize
126125
if kubeDefaultCacheSize != 100 {
127-
t.Fatalf("upstream DefaultWatchCacheSize changed from 100 to %q", kubeDefaultCacheSize)
126+
t.Fatalf("upstream DefaultWatchCacheSize changed to %d", kubeDefaultCacheSize)
128127
}
129-
testWatchCacheWithConfig(t, master, serverkube.DefaultWatchCacheSize, kubeDefaultCacheSize)
128+
if master.KubernetesMasterConfig.APIServerArguments == nil {
129+
master.KubernetesMasterConfig.APIServerArguments = configapi.ExtendedArguments{}
130+
}
131+
master.KubernetesMasterConfig.APIServerArguments["watch-cache-sizes"] = []string{"namespaces#100"}
132+
testWatchCacheWithConfig(t, master, 100, 0)
130133
}
131134

132135
func TestWatchCacheSizeWithFlag(t *testing.T) {
@@ -140,5 +143,5 @@ func TestWatchCacheSizeWithFlag(t *testing.T) {
140143
}
141144
master.KubernetesMasterConfig.APIServerArguments["watch-cache-sizes"] = []string{"namespaces#2000"}
142145

143-
testWatchCacheWithConfig(t, master, 2000, serverkube.DefaultWatchCacheSize)
146+
testWatchCacheWithConfig(t, master, 2000, 0)
144147
}

vendor/k8s.io/kubernetes/cmd/kube-apiserver/app/server.go

+11-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/k8s.io/kubernetes/federation/cmd/federation-apiserver/app/server.go

+12-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/k8s.io/kubernetes/federation/registry/cluster/etcd/etcd.go

-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/k8s.io/kubernetes/pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage/storage.go

-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/storage/storage.go

-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/k8s.io/kubernetes/pkg/registry/apps/controllerrevision/storage/storage.go

-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)