Skip to content

Commit 8a557bd

Browse files
committed
reduce unnecessary watch events in execution-status-controller
Signed-off-by: rohan-019 <[email protected]>
1 parent 78fa8ee commit 8a557bd

File tree

2 files changed

+126
-5
lines changed

2 files changed

+126
-5
lines changed

pkg/controllers/execution/execution_controller.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"sigs.k8s.io/controller-runtime/pkg/client"
3737
"sigs.k8s.io/controller-runtime/pkg/controller"
3838
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
39+
"sigs.k8s.io/controller-runtime/pkg/event"
3940
"sigs.k8s.io/controller-runtime/pkg/predicate"
4041

4142
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
@@ -133,21 +134,44 @@ func (c *Controller) Reconcile(ctx context.Context, req controllerruntime.Reques
133134

134135
// SetupWithManager creates a controller and register to controller manager.
135136
func (c *Controller) SetupWithManager(mgr controllerruntime.Manager) error {
136-
ctrlBuilder := controllerruntime.NewControllerManagedBy(mgr).Named(ControllerName).
137-
WithEventFilter(predicate.GenerationChangedPredicate{}).
138-
WithOptions(controller.Options{
137+
ctrlBuilder := controllerruntime.NewControllerManagedBy(mgr).Named(ControllerName).
138+
WithOptions(controller.Options{
139139
RateLimiter: ratelimiterflag.DefaultControllerRateLimiter[controllerruntime.Request](c.RateLimiterOptions),
140140
})
141141

142+
// Build the default predicate: allow generation changes and nil->non-nil deletion timestamp transitions; ignore delete events.
143+
defaultPred := buildWorkEventPredicate()
144+
142145
if c.WorkPredicateFunc != nil {
143-
ctrlBuilder.For(&workv1alpha1.Work{}, builder.WithPredicates(c.WorkPredicateFunc))
146+
ctrlBuilder.For(&workv1alpha1.Work{}, builder.WithPredicates(c.WorkPredicateFunc, defaultPred))
144147
} else {
145-
ctrlBuilder.For(&workv1alpha1.Work{})
148+
ctrlBuilder.For(&workv1alpha1.Work{}, builder.WithPredicates(defaultPred))
146149
}
147150

148151
return ctrlBuilder.Complete(c)
149152
}
150153

154+
// buildWorkEventPredicate returns the default predicate used to watch Work objects:
155+
// - Reconcile on generation changes
156+
// - Reconcile when deletion timestamp changes from nil to non-nil (finalizer path)
157+
// - Ignore delete events
158+
func buildWorkEventPredicate() predicate.Predicate {
159+
return predicate.Funcs{
160+
UpdateFunc: func(e event.UpdateEvent) bool {
161+
if e.ObjectOld == nil || e.ObjectNew == nil {
162+
return false
163+
}
164+
// Trigger when deletion timestamp transitions from nil to non-nil
165+
if e.ObjectOld.GetDeletionTimestamp().IsZero() && !e.ObjectNew.GetDeletionTimestamp().IsZero() {
166+
return true
167+
}
168+
// Trigger on generation change
169+
return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
170+
},
171+
DeleteFunc: func(event.DeleteEvent) bool { return false },
172+
}
173+
}
174+
151175
func (c *Controller) syncWork(ctx context.Context, clusterName string, work *workv1alpha1.Work) (controllerruntime.Result, error) {
152176
start := time.Now()
153177
err := c.syncToClusters(ctx, clusterName, work)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
Copyright 2025 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package execution
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"sigs.k8s.io/controller-runtime/pkg/event"
25+
26+
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
27+
)
28+
29+
// newWorkWithGenAndDeletion returns a Work with the specified generation and deletion timestamp presence.
30+
func newWorkWithGenAndDeletion(generation int64, deleted bool) *workv1alpha1.Work {
31+
w := &workv1alpha1.Work{}
32+
w.SetGeneration(generation)
33+
if deleted {
34+
now := metav1.Now()
35+
w.SetDeletionTimestamp(&now)
36+
}
37+
return w
38+
}
39+
40+
func TestBuildWorkEventPredicate_Update_GenerationChange(t *testing.T) {
41+
pred := buildWorkEventPredicate()
42+
43+
oldObj := newWorkWithGenAndDeletion(1, false)
44+
newObj := newWorkWithGenAndDeletion(2, false)
45+
46+
allow := pred.Update(event.UpdateEvent{ObjectOld: oldObj, ObjectNew: newObj})
47+
assert.True(t, allow)
48+
}
49+
50+
func TestBuildWorkEventPredicate_Update_DeletionTimestamp_NilToNonNil(t *testing.T) {
51+
pred := buildWorkEventPredicate()
52+
53+
oldObj := newWorkWithGenAndDeletion(1, false)
54+
newObj := newWorkWithGenAndDeletion(1, true)
55+
56+
allow := pred.Update(event.UpdateEvent{ObjectOld: oldObj, ObjectNew: newObj})
57+
assert.True(t, allow)
58+
}
59+
60+
func TestBuildWorkEventPredicate_Update_DeletionTimestamp_NonNilToNonNil(t *testing.T) {
61+
pred := buildWorkEventPredicate()
62+
63+
oldObj := newWorkWithGenAndDeletion(1, true)
64+
newObj := newWorkWithGenAndDeletion(1, true)
65+
66+
allow := pred.Update(event.UpdateEvent{ObjectOld: oldObj, ObjectNew: newObj})
67+
assert.False(t, allow)
68+
}
69+
70+
func TestBuildWorkEventPredicate_Update_NoChange(t *testing.T) {
71+
pred := buildWorkEventPredicate()
72+
73+
oldObj := newWorkWithGenAndDeletion(1, false)
74+
newObj := newWorkWithGenAndDeletion(1, false)
75+
76+
allow := pred.Update(event.UpdateEvent{ObjectOld: oldObj, ObjectNew: newObj})
77+
assert.False(t, allow)
78+
}
79+
80+
func TestBuildWorkEventPredicate_Delete_Ignored(t *testing.T) {
81+
pred := buildWorkEventPredicate()
82+
83+
obj := newWorkWithGenAndDeletion(1, true)
84+
allow := pred.Delete(event.DeleteEvent{Object: obj})
85+
assert.False(t, allow)
86+
}
87+
88+
func TestBuildWorkEventPredicate_Update_NilSafety(t *testing.T) {
89+
pred := buildWorkEventPredicate()
90+
91+
obj := newWorkWithGenAndDeletion(1, false)
92+
93+
assert.False(t, pred.Update(event.UpdateEvent{ObjectOld: nil, ObjectNew: obj}))
94+
assert.False(t, pred.Update(event.UpdateEvent{ObjectOld: obj, ObjectNew: nil}))
95+
}
96+
97+

0 commit comments

Comments
 (0)