Skip to content

Commit 801e684

Browse files
committed
Fix 504 timeout: Add K8s client caching + QPS increase + benchmarks
Signed-off-by: shovan-mondal <shovanmondal2004@gmail.com>
1 parent 3d845b3 commit 801e684

3 files changed

Lines changed: 423 additions & 24 deletions

File tree

chaoscenter/subscriber/pkg/k8s/client.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package k8s
22

33
import (
4+
"sync"
5+
"time"
6+
47
wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned"
58
v1alpha12 "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
69
"k8s.io/client-go/discovery"
@@ -10,24 +13,55 @@ import (
1013
"k8s.io/client-go/tools/clientcmd"
1114
)
1215

13-
var KubeConfig *string
16+
var (
17+
KubeConfig *string
18+
19+
// Singleton clientset to avoid creating new clients per request
20+
clientsetOnce sync.Once
21+
clientsetInstance *kubernetes.Clientset
22+
clientsetErr error
23+
)
1424

1525
// getKubeConfig setup the config for access cluster resource
1626
func (k8s *k8sSubscriber) GetKubeConfig() (*rest.Config, error) {
27+
var config *rest.Config
28+
var err error
29+
1730
// Use in-cluster config if kubeconfig path is not specified
1831
if *KubeConfig == "" {
19-
return rest.InClusterConfig()
32+
config, err = rest.InClusterConfig()
33+
} else {
34+
config, err = clientcmd.BuildConfigFromFlags("", *KubeConfig)
2035
}
21-
return clientcmd.BuildConfigFromFlags("", *KubeConfig)
22-
}
2336

24-
func (k8s *k8sSubscriber) GetGenericK8sClient() (*kubernetes.Clientset, error) {
25-
config, err := k8s.GetKubeConfig()
2637
if err != nil {
2738
return nil, err
2839
}
2940

30-
return kubernetes.NewForConfig(config)
41+
// Default QPS=5 and Burst=10 are too low for clusters with 100+ namespaces
42+
config.QPS = 50.0
43+
config.Burst = 100
44+
config.Timeout = 30 * time.Second
45+
46+
return config, nil
47+
}
48+
49+
func (k8s *k8sSubscriber) GetGenericK8sClient() (*kubernetes.Clientset, error) {
50+
// This eliminates TCP handshake and TLS negotiation overhead
51+
clientsetOnce.Do(func() {
52+
config, err := k8s.GetKubeConfig()
53+
if err != nil {
54+
clientsetErr = err
55+
return
56+
}
57+
clientsetInstance, clientsetErr = kubernetes.NewForConfig(config)
58+
})
59+
60+
if clientsetErr != nil {
61+
return nil, clientsetErr
62+
}
63+
64+
return clientsetInstance, nil
3165
}
3266

3367
// This function returns dynamic client and discovery client

chaoscenter/subscriber/pkg/k8s/objects.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"os"
99
"strings"
10+
"time"
1011

1112
"github.com/sirupsen/logrus"
1213

@@ -15,7 +16,6 @@ import (
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617
"k8s.io/apimachinery/pkg/runtime/schema"
1718
"k8s.io/client-go/dynamic"
18-
"k8s.io/client-go/kubernetes"
1919
)
2020

2121
var (
@@ -35,30 +35,41 @@ func (k8s *k8sSubscriber) GetKubernetesNamespaces(request types.KubeNamespaceReq
3535
}
3636
namespaceData = append(namespaceData, KubeNamespace)
3737
} else {
38-
// In case of cluster scope, get all the namespaces
39-
conf, err := k8s.GetKubeConfig()
40-
if err != nil {
41-
return nil, err
42-
}
43-
clientset, err := kubernetes.NewForConfig(conf)
38+
// Cached clientset instead of creating new one
39+
clientset, err := k8s.GetGenericK8sClient()
4440
if err != nil {
4541
return nil, err
4642
}
4743

48-
namespace, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
49-
if err != nil {
50-
return nil, err
44+
// Add context timeout to prevent indefinite hangs
45+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
46+
defer cancel()
47+
48+
// Add pagination support for clusters with many namespaces
49+
listOpts := metav1.ListOptions{
50+
Limit: 500, // Fetch in batches of 500
5151
}
52-
if len(namespace.Items) > 0 {
53-
for _, namespace := range namespace.Items {
5452

55-
KubeNamespace := &types.KubeNamespace{
56-
Name: namespace.GetName(),
57-
}
53+
for {
54+
namespaceList, err := clientset.CoreV1().Namespaces().List(ctx, listOpts)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
for _, ns := range namespaceList.Items {
60+
namespaceData = append(namespaceData, &types.KubeNamespace{
61+
Name: ns.GetName(),
62+
})
63+
}
5864

59-
namespaceData = append(namespaceData, KubeNamespace)
65+
// Check if there are more results to fetch
66+
if namespaceList.Continue == "" {
67+
break
6068
}
61-
} else {
69+
listOpts.Continue = namespaceList.Continue
70+
}
71+
72+
if len(namespaceData) == 0 {
6273
return nil, errors.New("No namespace available")
6374
}
6475
}

0 commit comments

Comments
 (0)