Skip to content

✨ Deduplicate events before sending them into the workqueue.#1390

Merged
k8s-ci-robot merged 4 commits into
kubernetes-sigs:masterfrom
coderanger:dedup
Mar 14, 2021
Merged

✨ Deduplicate events before sending them into the workqueue.#1390
k8s-ci-robot merged 4 commits into
kubernetes-sigs:masterfrom
coderanger:dedup

Conversation

@coderanger
Copy link
Copy Markdown
Contributor

This avoids race conditions where extra reconciles can happen rarely.

I still think kubernetes/kubernetes#99185 is a better solution but failing that, here is a "userspace" solution.

This avoids race conditions where extra reconciles can happen rarely.
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 21, 2021
@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Feb 21, 2021
@coderanger
Copy link
Copy Markdown
Contributor Author

Replaces #1380 but covers all three handler types instead.

@coderanger
Copy link
Copy Markdown
Contributor Author

If we got with this approach, should probably rewrite this to use k8s.io/apimachinery/pkg/util/sets.

Comment thread pkg/handler/enqueue_mapped.go Outdated
Also make sure that enqueue_mapped preserves order.
@coderanger
Copy link
Copy Markdown
Contributor Author

Will keep running them until they fail.
This was attempt #107
No, seriously... you can probably stop now.

Should be good on ordering. Still leaving owners as unordered since the underlying order is meaningless and can be shuffled.

// Delete implements EventHandler
func (e *EnqueueRequestForOwner) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
for _, req := range e.getOwnerReconcileRequest(evt.Object) {
reqs := map[reconcile.Request]empty{}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed here and in Generic? I would have assumed that kube doesn't allow multiple identical ownerRefs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't actually know what happens if you try to give the same non-controller owner twice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I bet you could have duplicates, +patchMergeKey=uid so it will theoretically dedup on UID but you could have invalid owner refs with the wrong UID and duplicate names? I would also be 100% fine with not handling that cleanly and risking spurious reconciles since that should be very hard to make happen.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I would prefer to omit that here and in Generic

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not in Delete nor Generic, why have it in Create?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code again, I think we should probably just leave it. It would complicate the flow to only do it sometimes, would have to switch back to returning a slice and then process it again, the extra memory allocations and code complexity seems not worth it.

Comment thread pkg/handler/eventhandler_test.go
Comment thread pkg/handler/enqueue_mapped.go Outdated
Comment thread pkg/handler/enqueue_owner.go
@cbandy
Copy link
Copy Markdown
Contributor

cbandy commented Mar 4, 2021

🦆 I tried to wrap my head around if/why we should dedupe in the single-object events (Create, Delete, Generic). It seems to me that if we do it in the Update event, we should do it in all events, right?

🦆 Should a user expect EnqueueRequestsFromMapFunc to dedupe the one slice their MapFunc returns? If we do it in Update, would it be unexpected for the other events to not?

🦆 Perhaps MapFunc is what it is and there should be a new EventHandler implementation that deals in unordered sets of Request. We could pass in a mutable set and they could call set.Insert themselves. That might avoid one slice and an iteration. (e.g. the Owner implementation here.)

🦆 If workqueue starts accepting slices, we would want to stick with slices... 😖 In that case, the two EventHandler implementations could (would?) have the same semantics.

❓ If workqueue starts accepting slices, would we definitely append the two MapFunc slices in Update and pass them to the queue at once?


🤔 after all that...

I think we should dedupe as little or as much as we like. Any amount is an improvement and approaches the ideal, where it happens directly in workqueue.

Perhaps we should change the documentation on any/all of these Handlers now to allow deduplication to change over time.

@alvaroaleman
Copy link
Copy Markdown
Member

I tried to wrap my head around if/why we should dedupe in the single-object events (Create, Delete, Generic). It seems to me that if we do it in the Update event, we should do it in all events, right?

Update events contain two objects, old obj and new obj which is why there is a good chance this might happen. Adding de-duplication for anything else is essentially assuming that users provide buggy mappers. I am apposed to that.

Should a user expect EnqueueRequestsFromMapFunc to dedupe the one slice their MapFunc returns? If we do it in Update, would it be unexpected for the other events to not?

No, see above.

Perhaps MapFunc is what it is and there should be a new EventHandler implementation that deals in unordered sets of Request. We could pass in a mutable set and they could call set.Insert themselves. That might avoid one slice and an iteration. (e.g. the Owner implementation here.)

I am opposed to that. What this change is doing in the first place is a performance optimization that for most controllers shouldn't matter. I am strictly opposed to complicating our API and dev user experience even the slightest for this. Adding a redundant way to do the same thing would fall into the "complicating API" category.

If workqueue starts accepting slices, we would want to stick with slices...  In that case, the two EventHandler implementations could (would?) have the same semantics.

It won't, they rejected the upstream PR.

@coderanger
Copy link
Copy Markdown
Contributor Author

@alvaroaleman I think this should be all set now?

Copy link
Copy Markdown
Member

@alvaroaleman alvaroaleman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/label tide/merge-method-squash
/lgtm
/approve

Sorry, I didn't have time initially and then it fell off my radar 🙃

@k8s-ci-robot k8s-ci-robot added the tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. label Mar 14, 2021
@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Mar 14, 2021
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alvaroaleman, coderanger

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 14, 2021
@alvaroaleman
Copy link
Copy Markdown
Member

/retest

1 similar comment
@alvaroaleman
Copy link
Copy Markdown
Member

/retest

@k8s-ci-robot k8s-ci-robot merged commit 253f275 into kubernetes-sigs:master Mar 14, 2021
2uasimojo added a commit to 2uasimojo/hive that referenced this pull request Jul 13, 2021
Update tests for controller-runtime changes from 0.8.2 to 0.9.2(+).

Most of these changes react to
kubernetes-sigs/controller-runtime#1399, which
made the fake client's Delete behave more like the real thing:
- If the object has finalizers, set `DeletionTimestamp` but don't
actually delete.
- If there are no finalizers, actually delete.
- Updates that remove the last finalizer from an object with a
`DeletionTimestamp` actually delete the object.

Then there's
kubernetes-sigs/controller-runtime#1390 which
deduplicates objects in the reconcile queue for Update actions.
RainbowMango pushed a commit to RainbowMango/controller-runtime that referenced this pull request Sep 1, 2021
…tes-sigs#1390)

* ✨ Deduplicate events before sending them into the workqueue.

This avoids race conditions where extra reconciles can happen rarely.

* ✨ Switch to map[string]struct{} to reduce memory usage slightly.

Also make sure that enqueue_mapped preserves order.

* 📝 Update function doc for getOwnerReconcileRequest.

* 🎨 Fix up duplication tests and ensure Update for _mapped dedups between both objects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants