Skip to content

Commit 38b91d3

Browse files
committed
Added Service & PVC cache for faster inspection loops
1 parent bd64ca7 commit 38b91d3

File tree

6 files changed

+184
-42
lines changed

6 files changed

+184
-42
lines changed

pkg/deployment/resources/pvcs.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,11 @@ func (r *Resources) EnsurePVCs() error {
4646
status, _ := r.context.GetStatus()
4747
enforceAntiAffinity := r.context.GetSpec().GetEnvironment().IsProduction()
4848

49-
pvcs := kubecli.CoreV1().PersistentVolumeClaims(ns)
50-
list, err := pvcs.List(metav1.ListOptions{})
51-
if err != nil {
52-
return maskAny(err)
53-
}
54-
pvcExists := func(name string) bool {
55-
for _, pvc := range list.Items {
56-
if pvc.GetName() == name {
57-
return true
58-
}
59-
}
60-
return false
61-
}
49+
pvcs := k8sutil.NewPersistentVolumeClaimCache(kubecli.CoreV1().PersistentVolumeClaims(ns))
6250
if err := iterator.ForeachServerGroup(func(group api.ServerGroup, spec api.ServerGroupSpec, status *api.MemberStatusList) error {
6351
for _, m := range *status {
6452
if m.PersistentVolumeClaimName != "" {
65-
if !pvcExists(m.PersistentVolumeClaimName) {
53+
if _, err := pvcs.Get(m.PersistentVolumeClaimName, metav1.GetOptions{}); err != nil {
6654
storageClassName := spec.GetStorageClassName()
6755
role := group.AsRole()
6856
resources := spec.Resources

pkg/deployment/resources/services.go

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,9 @@ func (r *Resources) EnsureServices() error {
4646
spec := r.context.GetSpec()
4747

4848
// Fetch existing services
49-
svcs := kubecli.CoreV1().Services(ns)
50-
list, err := svcs.List(metav1.ListOptions{})
51-
if err != nil {
52-
log.Debug().Err(err).Msg("Failed to list existing services")
53-
return maskAny(err)
54-
}
55-
svcExists := func(name string) bool {
56-
for _, svc := range list.Items {
57-
if svc.GetName() == name {
58-
return true
59-
}
60-
}
61-
return false
62-
}
63-
49+
svcs := k8sutil.NewServiceCache(kubecli.CoreV1().Services(ns))
6450
// Headless service
65-
if !svcExists(k8sutil.CreateHeadlessServiceName(deploymentName)) {
51+
if _, err := svcs.Get(k8sutil.CreateHeadlessServiceName(deploymentName), metav1.GetOptions{}); err != nil {
6652
svcName, newlyCreated, err := k8sutil.CreateHeadlessService(svcs, apiObject, owner)
6753
if err != nil {
6854
log.Debug().Err(err).Msg("Failed to create headless service")
@@ -75,7 +61,7 @@ func (r *Resources) EnsureServices() error {
7561

7662
// Internal database client service
7763
single := spec.GetMode().HasSingleServers()
78-
if !svcExists(k8sutil.CreateDatabaseClientServiceName(deploymentName)) {
64+
if _, err := svcs.Get(k8sutil.CreateDatabaseClientServiceName(deploymentName), metav1.GetOptions{}); err != nil {
7965
svcName, newlyCreated, err := k8sutil.CreateDatabaseClientService(svcs, apiObject, single, owner)
8066
if err != nil {
8167
log.Debug().Err(err).Msg("Failed to create database client service")
@@ -101,15 +87,15 @@ func (r *Resources) EnsureServices() error {
10187
if single {
10288
role = "single"
10389
}
104-
if err := r.ensureExternalAccessServices(eaServiceName, ns, role, "database", k8sutil.ArangoPort, false, spec.ExternalAccess, apiObject, log, kubecli); err != nil {
90+
if err := r.ensureExternalAccessServices(svcs, eaServiceName, ns, role, "database", k8sutil.ArangoPort, false, spec.ExternalAccess, apiObject, log, kubecli); err != nil {
10591
return maskAny(err)
10692
}
10793

10894
if spec.Sync.IsEnabled() {
10995
// External (and internal) Sync master service
11096
eaServiceName := k8sutil.CreateSyncMasterClientServiceName(deploymentName)
11197
role := "syncmaster"
112-
if err := r.ensureExternalAccessServices(eaServiceName, ns, role, "sync", k8sutil.ArangoSyncMasterPort, true, spec.Sync.ExternalAccess.ExternalAccessSpec, apiObject, log, kubecli); err != nil {
98+
if err := r.ensureExternalAccessServices(svcs, eaServiceName, ns, role, "sync", k8sutil.ArangoSyncMasterPort, true, spec.Sync.ExternalAccess.ExternalAccessSpec, apiObject, log, kubecli); err != nil {
11399
return maskAny(err)
114100
}
115101
status, lastVersion := r.context.GetStatus()
@@ -124,12 +110,11 @@ func (r *Resources) EnsureServices() error {
124110
}
125111

126112
// EnsureServices creates all services needed to service the deployment
127-
func (r *Resources) ensureExternalAccessServices(eaServiceName, ns, svcRole, title string, port int, noneIsClusterIP bool, spec api.ExternalAccessSpec, apiObject k8sutil.APIObject, log zerolog.Logger, kubecli kubernetes.Interface) error {
113+
func (r *Resources) ensureExternalAccessServices(svcs k8sutil.ServiceInterface, eaServiceName, ns, svcRole, title string, port int, noneIsClusterIP bool, spec api.ExternalAccessSpec, apiObject k8sutil.APIObject, log zerolog.Logger, kubecli kubernetes.Interface) error {
128114
// Database external access service
129115
createExternalAccessService := false
130116
deleteExternalAccessService := false
131117
eaServiceType := spec.GetType().AsServiceType() // Note: Type auto defaults to ServiceTypeLoadBalancer
132-
svcs := kubecli.CoreV1().Services(ns)
133118
if existing, err := svcs.Get(eaServiceName, metav1.GetOptions{}); err == nil {
134119
// External access service exists
135120
loadBalancerIP := spec.GetLoadBalancerIP()

pkg/util/k8sutil/pvc.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@ import (
2727

2828
"k8s.io/api/core/v1"
2929
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30-
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
3130

3231
"github.com/arangodb/kube-arangodb/pkg/util/constants"
3332
)
3433

34+
// PersistentVolumeClaimInterface has methods to work with PersistentVolumeClaim resources.
35+
type PersistentVolumeClaimInterface interface {
36+
Create(*v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error)
37+
Get(name string, options metav1.GetOptions) (*v1.PersistentVolumeClaim, error)
38+
}
39+
3540
// IsPersistentVolumeClaimMarkedForDeletion returns true if the pod has been marked for deletion.
3641
func IsPersistentVolumeClaimMarkedForDeletion(pvc *v1.PersistentVolumeClaim) bool {
3742
return pvc.DeletionTimestamp != nil
@@ -46,7 +51,7 @@ func CreatePersistentVolumeClaimName(deploymentName, role, id string) string {
4651
// CreatePersistentVolumeClaim creates a persistent volume claim with given name and configuration.
4752
// If the pvc already exists, nil is returned.
4853
// If another error occurs, that error is returned.
49-
func CreatePersistentVolumeClaim(pvcs corev1.PersistentVolumeClaimInterface, pvcName, deploymentName, ns, storageClassName, role string, enforceAntiAffinity bool, resources v1.ResourceRequirements, finalizers []string, owner metav1.OwnerReference) error {
54+
func CreatePersistentVolumeClaim(pvcs PersistentVolumeClaimInterface, pvcName, deploymentName, ns, storageClassName, role string, enforceAntiAffinity bool, resources v1.ResourceRequirements, finalizers []string, owner metav1.OwnerReference) error {
5055
labels := LabelsForDeployment(deploymentName, role)
5156
volumeMode := v1.PersistentVolumeFilesystem
5257
pvc := &v1.PersistentVolumeClaim{

pkg/util/k8sutil/pvc_cache.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package k8sutil
24+
25+
import (
26+
"k8s.io/api/core/v1"
27+
apierrors "k8s.io/apimachinery/pkg/api/errors"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/runtime/schema"
30+
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
31+
)
32+
33+
// pvcsCache implements a cached version of a PersistentVolumeClaimInterface.
34+
// It is NOT go-routine safe.
35+
type pvcsCache struct {
36+
cli corev1.PersistentVolumeClaimInterface
37+
cache []v1.PersistentVolumeClaim
38+
}
39+
40+
// NewPersistentVolumeClaimCache creates a cached version of the given PersistentVolumeClaimInterface.
41+
func NewPersistentVolumeClaimCache(cli corev1.PersistentVolumeClaimInterface) PersistentVolumeClaimInterface {
42+
return &pvcsCache{cli: cli}
43+
}
44+
45+
var (
46+
pvcGroupResource = schema.GroupResource{
47+
Group: v1.GroupName,
48+
Resource: "PersistentVolumeClaim",
49+
}
50+
)
51+
52+
func (sc *pvcsCache) Create(s *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {
53+
sc.cache = nil
54+
result, err := sc.cli.Create(s)
55+
if err != nil {
56+
return nil, maskAny(err)
57+
}
58+
return result, nil
59+
}
60+
61+
func (sc *pvcsCache) Get(name string, options metav1.GetOptions) (*v1.PersistentVolumeClaim, error) {
62+
if sc.cache == nil {
63+
list, err := sc.cli.List(metav1.ListOptions{})
64+
if err != nil {
65+
return nil, maskAny(err)
66+
}
67+
sc.cache = list.Items
68+
}
69+
for _, s := range sc.cache {
70+
if s.GetName() == name {
71+
return &s, nil
72+
}
73+
}
74+
return nil, maskAny(apierrors.NewNotFound(pvcGroupResource, name))
75+
}

pkg/util/k8sutil/services.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,15 @@ import (
3131

3232
"k8s.io/api/core/v1"
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34-
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
3534
)
3635

36+
// ServiceInterface has methods to work with Service resources.
37+
type ServiceInterface interface {
38+
Create(*v1.Service) (*v1.Service, error)
39+
Delete(name string, options *metav1.DeleteOptions) error
40+
Get(name string, options metav1.GetOptions) (*v1.Service, error)
41+
}
42+
3743
// CreateHeadlessServiceName returns the name of the headless service for the given
3844
// deployment name.
3945
func CreateHeadlessServiceName(deploymentName string) string {
@@ -63,7 +69,7 @@ func CreateSyncMasterClientServiceName(deploymentName string) string {
6369
// If the service already exists, nil is returned.
6470
// If another error occurs, that error is returned.
6571
// The returned bool is true if the service is created, or false when the service already existed.
66-
func CreateHeadlessService(svcs corev1.ServiceInterface, deployment metav1.Object, owner metav1.OwnerReference) (string, bool, error) {
72+
func CreateHeadlessService(svcs ServiceInterface, deployment metav1.Object, owner metav1.OwnerReference) (string, bool, error) {
6773
deploymentName := deployment.GetName()
6874
svcName := CreateHeadlessServiceName(deploymentName)
6975
ports := []v1.ServicePort{
@@ -86,7 +92,7 @@ func CreateHeadlessService(svcs corev1.ServiceInterface, deployment metav1.Objec
8692
// If the service already exists, nil is returned.
8793
// If another error occurs, that error is returned.
8894
// The returned bool is true if the service is created, or false when the service already existed.
89-
func CreateDatabaseClientService(svcs corev1.ServiceInterface, deployment metav1.Object, single bool, owner metav1.OwnerReference) (string, bool, error) {
95+
func CreateDatabaseClientService(svcs ServiceInterface, deployment metav1.Object, single bool, owner metav1.OwnerReference) (string, bool, error) {
9096
deploymentName := deployment.GetName()
9197
svcName := CreateDatabaseClientServiceName(deploymentName)
9298
ports := []v1.ServicePort{
@@ -115,7 +121,7 @@ func CreateDatabaseClientService(svcs corev1.ServiceInterface, deployment metav1
115121
// If the service already exists, nil is returned.
116122
// If another error occurs, that error is returned.
117123
// The returned bool is true if the service is created, or false when the service already existed.
118-
func CreateExternalAccessService(svcs corev1.ServiceInterface, svcName, role string, deployment metav1.Object, serviceType v1.ServiceType, port, nodePort int, loadBalancerIP string, owner metav1.OwnerReference) (string, bool, error) {
124+
func CreateExternalAccessService(svcs ServiceInterface, svcName, role string, deployment metav1.Object, serviceType v1.ServiceType, port, nodePort int, loadBalancerIP string, owner metav1.OwnerReference) (string, bool, error) {
119125
deploymentName := deployment.GetName()
120126
ports := []v1.ServicePort{
121127
v1.ServicePort{
@@ -137,7 +143,7 @@ func CreateExternalAccessService(svcs corev1.ServiceInterface, svcName, role str
137143
// If the service already exists, nil is returned.
138144
// If another error occurs, that error is returned.
139145
// The returned bool is true if the service is created, or false when the service already existed.
140-
func createService(svcs corev1.ServiceInterface, svcName, deploymentName, ns, clusterIP, role string, serviceType v1.ServiceType,
146+
func createService(svcs ServiceInterface, svcName, deploymentName, ns, clusterIP, role string, serviceType v1.ServiceType,
141147
ports []v1.ServicePort, loadBalancerIP string, publishNotReadyAddresses bool, owner metav1.OwnerReference) (bool, error) {
142148
labels := LabelsForDeployment(deploymentName, role)
143149
svc := &v1.Service{

pkg/util/k8sutil/services_cache.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package k8sutil
24+
25+
import (
26+
"k8s.io/api/core/v1"
27+
apierrors "k8s.io/apimachinery/pkg/api/errors"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/runtime/schema"
30+
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
31+
)
32+
33+
// servicesCache implements a cached version of a ServiceInterface.
34+
// It is NOT go-routine safe.
35+
type servicesCache struct {
36+
cli corev1.ServiceInterface
37+
cache []v1.Service
38+
}
39+
40+
// NewServiceCache creates a cached version of the given ServiceInterface.
41+
func NewServiceCache(cli corev1.ServiceInterface) ServiceInterface {
42+
return &servicesCache{cli: cli}
43+
}
44+
45+
var (
46+
serviceGroupResource = schema.GroupResource{
47+
Group: v1.GroupName,
48+
Resource: "Service",
49+
}
50+
)
51+
52+
func (sc *servicesCache) Create(s *v1.Service) (*v1.Service, error) {
53+
sc.cache = nil
54+
result, err := sc.cli.Create(s)
55+
if err != nil {
56+
return nil, maskAny(err)
57+
}
58+
return result, nil
59+
}
60+
61+
func (sc *servicesCache) Delete(name string, options *metav1.DeleteOptions) error {
62+
sc.cache = nil
63+
if err := sc.cli.Delete(name, options); err != nil {
64+
return maskAny(err)
65+
}
66+
return nil
67+
}
68+
69+
func (sc *servicesCache) Get(name string, options metav1.GetOptions) (*v1.Service, error) {
70+
if sc.cache == nil {
71+
list, err := sc.cli.List(metav1.ListOptions{})
72+
if err != nil {
73+
return nil, maskAny(err)
74+
}
75+
sc.cache = list.Items
76+
}
77+
for _, s := range sc.cache {
78+
if s.GetName() == name {
79+
return &s, nil
80+
}
81+
}
82+
return nil, maskAny(apierrors.NewNotFound(serviceGroupResource, name))
83+
}

0 commit comments

Comments
 (0)