Skip to content

Commit 7217df0

Browse files
committed
fix: address review - optimize List to O(1) lookup, filter deleting claims
- Extract podClaimedByOthers loop into buildPodClaimIndex() that builds a map[podName]claimName once per reconcile, reducing N API calls to 1 - Skip CNClaims with DeletionTimestamp != nil to prevent Pod leak when both claims are being deleted simultaneously - Add holder claim name to log messages for easier debugging - Add test for deleting claim filtering and buildPodClaimIndex
1 parent 376e2a4 commit 7217df0

2 files changed

Lines changed: 80 additions & 9 deletions

File tree

pkg/controllers/cnclaim/controller.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,16 @@ func (r *Actor) selectCN(ctx *recon.Context[*v1alpha1.CNClaim], orphans []corev1
159159
}
160160

161161
sortCNByPriority(c, idleCNs)
162+
// build index once: podName -> claimName for all other CNClaims
163+
claimIndex, err := buildPodClaimIndex(ctx, c.Namespace, c.Name)
164+
if err != nil {
165+
return nil, errors.WrapPrefix(err, "error building pod claim index", 0)
166+
}
162167
for i := range idleCNs {
163168
pod := &idleCNs[i]
164169
// skip pod already referenced by another CNClaim's spec.podName
165-
if claimed, err := podClaimedByOthers(ctx, c.Namespace, pod.Name, c.Name); err != nil {
166-
return nil, errors.WrapPrefix(err, "error checking pod claims", 0)
167-
} else if claimed {
168-
ctx.Log.Info("skip pod claimed by other CNClaim", "podName", pod.Name)
170+
if holder, ok := claimIndex[pod.Name]; ok {
171+
ctx.Log.Info("skip pod claimed by other CNClaim", "podName", pod.Name, "holder", holder)
169172
continue
170173
}
171174
if err := r.ensureOwnership(ctx, pod); err != nil {
@@ -347,13 +350,16 @@ func (r *Actor) Finalize(ctx *recon.Context[*v1alpha1.CNClaim]) (bool, error) {
347350
if len(ownedCNs) == 0 {
348351
return true, nil
349352
}
353+
// build index once: podName -> claimName for all other CNClaims
354+
claimIndex, err := buildPodClaimIndex(ctx, c.Namespace, c.Name)
355+
if err != nil {
356+
return false, errors.WrapPrefix(err, "error building pod claim index", 0)
357+
}
350358
for i := range ownedCNs {
351359
cn := ownedCNs[i]
352360
// skip reclaim if another CNClaim still references this pod via spec.podName
353-
if claimed, err := podClaimedByOthers(ctx, c.Namespace, cn.Name, c.Name); err != nil {
354-
return false, errors.WrapPrefix(err, "error checking pod claims", 0)
355-
} else if claimed {
356-
ctx.Log.Info("skip reclaim, pod still claimed by other CNClaim", "pod", cn.Name)
361+
if holder, ok := claimIndex[cn.Name]; ok {
362+
ctx.Log.Info("skip reclaim, pod still claimed by other CNClaim", "pod", cn.Name, "holder", holder)
357363
continue
358364
}
359365
ctx.Log.Info("finalize CNClaim, reclaim bound CN", "cn", cn.Name)
@@ -366,14 +372,15 @@ func (r *Actor) Finalize(ctx *recon.Context[*v1alpha1.CNClaim]) (bool, error) {
366372

367373
// podClaimedByOthers checks if the given pod is referenced by any CNClaim's
368374
// spec.podName other than excludeClaim in the same namespace.
375+
// It skips CNClaims that are being deleted (DeletionTimestamp != nil).
369376
func podClaimedByOthers(cli recon.KubeClient, namespace, podName, excludeClaim string) (bool, error) {
370377
claimList := &v1alpha1.CNClaimList{}
371378
if err := cli.List(claimList, client.InNamespace(namespace)); err != nil {
372379
return false, err
373380
}
374381
for i := range claimList.Items {
375382
claim := &claimList.Items[i]
376-
if claim.Name == excludeClaim {
383+
if claim.Name == excludeClaim || claim.DeletionTimestamp != nil {
377384
continue
378385
}
379386
if claim.Spec.PodName == podName {
@@ -383,6 +390,25 @@ func podClaimedByOthers(cli recon.KubeClient, namespace, podName, excludeClaim s
383390
return false, nil
384391
}
385392

393+
// buildPodClaimIndex lists all CNClaims in the namespace and returns a map
394+
// from podName to the claiming CNClaim name, excluding the given claim and
395+
// CNClaims that are being deleted.
396+
func buildPodClaimIndex(cli recon.KubeClient, namespace, excludeClaim string) (map[string]string, error) {
397+
claimList := &v1alpha1.CNClaimList{}
398+
if err := cli.List(claimList, client.InNamespace(namespace)); err != nil {
399+
return nil, err
400+
}
401+
index := make(map[string]string, len(claimList.Items))
402+
for i := range claimList.Items {
403+
claim := &claimList.Items[i]
404+
if claim.Name == excludeClaim || claim.DeletionTimestamp != nil || claim.Spec.PodName == "" {
405+
continue
406+
}
407+
index[claim.Spec.PodName] = claim.Name
408+
}
409+
return index, nil
410+
}
411+
386412
func (r *Actor) patchStore(ctx *recon.Context[*v1alpha1.CNClaim], pod *corev1.Pod, req logpb.CNStateLabel) (*metadata.CNService, error) {
387413
cs, err := common.ResolveCNSet(ctx, pod)
388414
if err != nil {

pkg/controllers/cnclaim/controller_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,23 @@ func Test_podClaimedByOthers(t *testing.T) {
141141
excludeClaim: "claim-a",
142142
want: false,
143143
},
144+
{
145+
name: "deleting claim is ignored",
146+
claims: []client.Object{
147+
&v1alpha1.CNClaim{
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "claim-deleting",
150+
Namespace: "ns",
151+
DeletionTimestamp: &metav1.Time{Time: metav1.Now().Time},
152+
Finalizers: []string{"test"},
153+
},
154+
Spec: v1alpha1.CNClaimSpec{ClaimPodRef: v1alpha1.ClaimPodRef{PodName: "pod-1"}},
155+
},
156+
},
157+
podName: "pod-1",
158+
excludeClaim: "claim-a",
159+
want: false,
160+
},
144161
}
145162
for _, tt := range tests {
146163
t.Run(tt.name, func(t *testing.T) {
@@ -153,6 +170,34 @@ func Test_podClaimedByOthers(t *testing.T) {
153170
}
154171
}
155172

173+
func Test_buildPodClaimIndex(t *testing.T) {
174+
g := NewGomegaWithT(t)
175+
now := metav1.Now()
176+
cli := newFakeClient(
177+
&v1alpha1.CNClaim{
178+
ObjectMeta: metav1.ObjectMeta{Name: "self", Namespace: "ns"},
179+
Spec: v1alpha1.CNClaimSpec{ClaimPodRef: v1alpha1.ClaimPodRef{PodName: "pod-1"}},
180+
},
181+
&v1alpha1.CNClaim{
182+
ObjectMeta: metav1.ObjectMeta{Name: "other", Namespace: "ns"},
183+
Spec: v1alpha1.CNClaimSpec{ClaimPodRef: v1alpha1.ClaimPodRef{PodName: "pod-2"}},
184+
},
185+
&v1alpha1.CNClaim{
186+
ObjectMeta: metav1.ObjectMeta{Name: "deleting", Namespace: "ns",
187+
DeletionTimestamp: &now, Finalizers: []string{"test"}},
188+
Spec: v1alpha1.CNClaimSpec{ClaimPodRef: v1alpha1.ClaimPodRef{PodName: "pod-3"}},
189+
},
190+
&v1alpha1.CNClaim{
191+
ObjectMeta: metav1.ObjectMeta{Name: "pending", Namespace: "ns"},
192+
Spec: v1alpha1.CNClaimSpec{},
193+
},
194+
)
195+
index, err := buildPodClaimIndex(&fakeKubeClient{cli}, "ns", "self")
196+
g.Expect(err).NotTo(HaveOccurred())
197+
// "self" excluded, "deleting" filtered, "pending" has no podName
198+
g.Expect(index).To(Equal(map[string]string{"pod-2": "other"}))
199+
}
200+
156201
func Test_sortCNByPriority(t *testing.T) {
157202
tests := []struct {
158203
name string

0 commit comments

Comments
 (0)