K8SPSMDB-1455: avoid repeated delete calls#2389
Conversation
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a small Kubernetes utility helper to make resource deletions idempotent and updates the main PSMDB controller to use it, aiming to avoid repeated delete attempts when objects are already gone.
Changes:
- Added
k8s.DeleteIfExists(ctx, client, obj)helper inpkg/k8s/utils.go. - Replaced several direct
Delete(...)/IsNotFound(...)patterns in the PSMDB controller withDeleteIfExists(arbiter SFS, non-voting SFS, config SFS/service, mongos SFS).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| pkg/k8s/utils.go | Adds DeleteIfExists helper used to skip deletion when the object is absent. |
| pkg/controller/perconaservermongodb/psmdb_controller.go | Switches multiple deletion call sites to use the new helper to reduce repeated delete calls. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func DeleteIfExists(ctx context.Context, c client.Client, obj client.Object) error { | ||
| if err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj); k8serrors.IsNotFound(err) { | ||
| return nil | ||
| } else if err != nil { | ||
| return errors.Wrapf(err, "failed to get object: %s", obj.GetName()) | ||
| } | ||
| return c.Delete(ctx, obj) |
| err = errors.Errorf("delete nonVoting statefulset %s: %v", replset.Name, err) | ||
| return err | ||
| if err := k8sutils.DeleteIfExists(ctx, r.client, psmdb.NewStatefulSet(naming.NonVotingStatefulSetName(cr, replset), cr.Namespace)); err != nil { | ||
| return errors.Wrapf(err, "failed to delete non voting statefulset: %s", naming.NonVotingStatefulSetName(cr, replset)) |
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
f449ee5 to
af24c9f
Compare
| client, err := client.New(mgr.GetConfig(), client.Options{ | ||
| Scheme: mgr.GetScheme(), | ||
| Cache: &client.CacheOptions{ | ||
| DisableFor: []client.Object{&corev1.Node{}}, | ||
| }, | ||
| }) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "create client") | ||
| } |
There was a problem hiding this comment.
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
| func DeleteIfExists(ctx context.Context, c client.Client, obj client.Object) error { | ||
| if err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj); k8serrors.IsNotFound(err) { | ||
| return nil | ||
| } else if err != nil { | ||
| return errors.Wrapf(err, "failed to get %T %s/%s", obj, obj.GetNamespace(), obj.GetName()) | ||
| } | ||
|
|
||
| if err := c.Delete(ctx, obj); client.IgnoreNotFound(err) != nil { | ||
| return errors.Wrapf(err, "failed to delete %T %s/%s", obj, obj.GetNamespace(), obj.GetName()) | ||
| } | ||
| return nil | ||
| } |
| } | ||
|
|
||
| return r.deleteMongos(ctx, cr) | ||
| if err := k8sutils.DeleteIfExists(ctx, r.client, psmdb.MongosStatefulset(cr)); err != nil { |
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
| func DeleteIfExists(ctx context.Context, c client.Client, obj client.Object) error { | ||
| if err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj); k8serrors.IsNotFound(err) { | ||
| return nil | ||
| } else if err != nil { | ||
| return errors.Wrapf(err, "failed to get %T %s/%s", obj, obj.GetNamespace(), obj.GetName()) | ||
| } | ||
|
|
||
| if err := c.Delete(ctx, obj); client.IgnoreNotFound(err) != nil { | ||
| return errors.Wrapf(err, "failed to delete %T %s/%s", obj, obj.GetNamespace(), obj.GetName()) | ||
| } | ||
| return nil | ||
| } |
| const eventRegardingNameIndex = "regarding.name" | ||
|
|
||
| func eventRegardingNameIndexer(o client.Object) []string { | ||
| return []string{o.(*eventsv1.Event).Regarding.Name} | ||
| } |
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
| func eventRegardingNameIndexer(o client.Object) []string { | ||
| return []string{o.(*eventsv1.Event).Regarding.Name} | ||
| } |
| func DeleteIfExists(ctx context.Context, c client.Client, obj client.Object) error { | ||
| if err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj); k8serrors.IsNotFound(err) { | ||
| return nil | ||
| } else if err != nil { | ||
| return errors.Wrapf(err, "failed to get %T %s/%s", obj, obj.GetNamespace(), obj.GetName()) | ||
| } | ||
|
|
||
| if err := c.Delete(ctx, obj); client.IgnoreNotFound(err) != nil { | ||
| return errors.Wrapf(err, "failed to delete %T %s/%s", obj, obj.GetNamespace(), obj.GetName()) | ||
| } | ||
| return nil | ||
| } |
| // the volume resize logic lists PVC events through the cached client, | ||
| // which requires the field to be indexed | ||
| err = mgr.GetFieldIndexer().IndexField(context.TODO(), &eventsv1.Event{}, eventRegardingNameIndex, eventRegardingNameIndexer) | ||
| if err != nil { | ||
| return errors.Wrapf(err, "index events by %s", eventRegardingNameIndex) | ||
| } |
|
|
||
| # user should be able to restore to the previous size and make the cluster ready | ||
| patch_pvc_request "${cluster}" "3G" | ||
| wait_pvc_request_revert "${cluster}" "3G" |
There was a problem hiding this comment.
We changed this behaviour in #2341
We don't land in an error state anymore, we instead revert the update in case of downscaling. This test was being very flaky
commit: 136576e |
CHANGE DESCRIPTION
Problem:
Fixes #1751
The operator makes repeated redundant
Deletecalls to KubeAPICause:
A lot of the clean-up logic makes unconditional
Deletecalls on every reconcile. This causes the operator to flood the API with unneededDeletecallsSolution:
DeleteIfExistshelper that deletes an object only if it exists. The initialGetcall hits the cache so effectively we are reducing the requests to the KubeAPICHECKLIST
Jira
Needs Doc) and QA (Needs QA)?Tests
compare/*-oc.yml)?Config/Logging/Testability