Skip to content

Using fake api client to query resources will cause the client-go package memory to continue to grow #129487

Closed as not planned
@enaguo

Description

@enaguo

What happened?

When I use fake api client in the simulated scheduler to simulate the resource changes of pods or nodes, the memory of the client-go package continues to increase.For example, after I create a pod, when I query this pod multiple times, the client-go memory continues to grow.

The problem I'm currently facing is that all actions such as query, modify, and delete will be appended to this c.actions slice, causing the client-go package memory to continue to increase until the process is restarted to release the memory.

 func (c *Fake) Invokes(action Action, defaultReturnObj runtime.Object) (runtime.Object, error) {

	c.Lock()
	defer c.Unlock()

	actionCopy := action.DeepCopy()
	c.actions = append(c.actions, action.DeepCopy())
	for _, reactor := range c.ReactionChain {
		if !reactor.Handles(actionCopy) {
			continue
		}

		handled, ret, err := reactor.React(actionCopy)
		if !handled {
			continue
		}

		return ret, err
	}

	return defaultReturnObj, nil
}

What did you expect to happen?

Fix the code here: c.actions = append(c.actions, action.DeepCopy()) , where actions such as add, delete, modify, and query are not continuously appended to a.action, causing the memory to continue to grow.

How can we reproduce it (as minimally and precisely as possible)?

import (
	"context"
	"fmt"
	"net/http"
	"time"

	_ "net/http/pprof"

	"github.com/gofrs/uuid"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	fakeclientset "k8s.io/client-go/kubernetes/fake"
)

func main() {
	go func() {
		fmt.Println(http.ListenAndServe("localhost:8065", nil))
	}()

	// 创建fakeclient
	fc := fakeclientset.NewSimpleClientset()
	for i := 0; i < 100000; i++ {
		// 随机生成pod的name
		uuidName, _ := uuid.NewV4()
		podName := "test-" + uuidName.String()
		// 随机创建pod
		pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: podName}}
		// 创建pod
		fc.CoreV1().Pods("default").Create(context.TODO(), pod, metav1.CreateOptions{})
		pod.Spec.NodeName = "node1"
		// 更新pod
		fc.CoreV1().Pods("default").Update(context.TODO(), pod, metav1.UpdateOptions{})
		// 删除pod
		err := fc.CoreV1().Pods("default").Delete(context.TODO(), podName, metav1.DeleteOptions{})
		if err != nil {
			fmt.Println("delete pod failed")
		}
	}
}

Anything else we need to know?

No response

Kubernetes version

$ kubectl version
Client Version: v1.29.1
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.29.

Cloud provider

Self hosted

OS version

# On Linux:
$ cat /etc/os-release
# paste output here
$ uname -a
# paste output here

# On Windows:
C:\> wmic os get Caption, Version, BuildNumber, OSArchitecture
# paste output here

Install tools

Container runtime (CRI) and version (if applicable)

Related plugins (CNI, CSI, ...) and versions (if applicable)

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.lifecycle/rottenDenotes an issue or PR that has aged beyond stale and will be auto-closed.needs-sigIndicates an issue or PR lacks a `sig/foo` label and requires one.needs-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions