Skip to content

stop watcher goroutines from outliving their owner - #13705

Merged
kubernetes-prow[bot] merged 3 commits into
kubernetes-sigs:mainfrom
andrewseif:13290-multikueue-race-repro
Aug 4, 2026
Merged

kubernetes-prow[bot] merged 3 commits into
kubernetes-sigs:mainfrom
andrewseif:13290-multikueue-race-repro

Conversation

@andrewseif

@andrewseif andrewseif commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind bug
/kind flake
/area multikueue

What this PR does / why we need it:

The multikueue unit-test package fails intermittently under -race as a package-level FAIL that no individual test explains. The cause is a watcher goroutine that outlives the test that started it and logs through that test's logger.

establishWatcher spawns a goroutine that logs as its very first statement, and again after
its loop ends:

return func() {
    go func() {
        log.V(2).Info("Starting watch")
        for r := range newWatcher.ResultChan() {
            ...
        }
        log.V(2).Info("Watch ended", "ctxErr", ctx.Err())

log comes from ctrl.LoggerFrom(ctx), which under test is a testr logger bound to
*testing.T. A watcher still running after its test returns calls t.Log() while tRunner
is writing to the same testing.common during teardown. The racing memory is Go's own
struct, which is why no Kueue-side test owns the failure.

This is an excerpt from the local logs

WARNING: DATA RACE
Read at 0x00c000cfc953 by goroutine 765:
  testing.(*common).destination()                       testing/testing.go:1064
  github.com/go-logr/logr/testr.logInfo()               testr/testr.go:101
  multikueue.(*remoteClient).establishWatcher.func1.1() multikueuecluster.go:422

Previous write at 0x00c000cfc953 by goroutine 451:
  testing.tRunner.func1()                               testing/testing.go:2023

Goroutine 765 (running) created at:
  multikueue.(*remoteClient).establishWatcher.func1()   multikueuecluster.go:421
  multikueue.(*remoteClient).updateConfigAndRefreshWatchers()
  multikueue.(*clustersReconciler).setRemoteClientConfig()
  multikueue.(*clustersReconciler).Reconcile()
  multikueue.TestUpdateConfig.func2()                   multikueuecluster_test.go:718
Goroutine 451 (finished)

There are two independent defects here, and neither fix works without the other.

1. StopWatchers could not stop anything (multikueuecluster.go).

It cancelled the watch context and returned. Cancellation is a request, not a join — and worse,
the watch loop never observed it, because cancelling the context does not close
newWatcher.ResultChan(). A watcher could therefore ignore cancellation indefinitely.

  • The loop now selects on ctx.Done() alongside the result channel.
  • remoteClient gains a sync.WaitGroup; the starter closure registers before spawning, so a
    concurrent StopWatchers never sees a zero counter while a goroutine is coming up.
  • StopWatchers joins the group after cancelling, outside rc.mu — watcher goroutines take
    that lock themselves via getClient, so joining under it would deadlock.
  • The two channel sends reachable from a watcher (queueWorkloadEvent, queueWatchEndedEvent)
    now yield to cancellation instead of blocking forever. This is required, not tidying:
    updateConfigAndRefreshWatchers calls StopWatchers while its caller holds
    updateConfigLock, so a watcher blocked sending on watchEndedCh would wait on a reconcile
    that is itself waiting on the join.

The resulting ordering is what closes the race: cancel → loop exits → the trailing
"Watch ended" log runs → deferred Done() → Wait() returns. Every log the goroutine emits
now completes before StopWatchers returns.

2. TestUpdateConfig never stopped anything (multikueuecluster_test.go).

The subtest builds a reconciler, calls Reconcile, which starts the watchers and returns.
It never calls StopWatchers, and utiltesting.ContextWithLog hands back no cancel func, so
nothing tears the watchers down. A t.Cleanup now joins every remote client the subtest
created. TestRemoteClientGC already used defer rc.StopWatchers(); this brings
TestUpdateConfig in line.

Which issue(s) this PR fixes:

Related #13290

Special notes for your reviewer:

I reproduced at the exact commit the failing CI run built,
d2a131a954b7ff703df5e87ec32902e3cedb00f9, taken from that run's clone-log.txt. The tree was
materialised with git archive and verified by content hash against a fresh archive of the same
commit (identical SHA-256 manifest); go mod verify passes against that commit's
own go.sum.

few things I tried while testing this:

  • Adding only the WaitGroup and the join deadlocked the unit suite, three watcher
    goroutines sat in chan receive for nine minutes until the package timed out. The
    WaitGroup did not cause that; it exposed goroutines that were already ignoring
    cancellation, unnoticed because nothing joined them. Hence the context-aware loop.
  • Fixing only the controller side still raced, on the first iteration of a fresh
    validation run. A join nothing calls changes nothing. Hence the test cleanup.

One last thing: the reproduction ran on linux/arm6, not on amd64.

Disclosure: this change was developed with AI assistance (Claude Code). The investigation, the change, and the verification were reviewed by me.

Does this PR introduce a user-facing change?

MultiKueue: Fixed an issue where remote-cluster watcher goroutines could continue running after a worker cluster was removed, disconnected, or reconfigured.

Summary by CodeRabbit

  • Bug Fixes
    • Improved shutdown handling for remote watchers.
    • Prevented blocked event delivery when watch contexts are canceled or channels close.
    • Ensured watcher processes finish cleanly during shutdown, improving resource cleanup and reliability.

@kubernetes-prow kubernetes-prow Bot added release-note-none Denotes a PR that doesn't merit a release note. kind/bug Categorizes issue or PR as related to a bug. kind/flake Categorizes issue or PR as related to a flaky test. area/multikueue Issues or PRs related to MultiKueue labels Jul 31, 2026
@netlify

netlify Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for kubernetes-sigs-kueue canceled.

Name Link
🔨 Latest commit 1cb991f
🔍 Latest deploy log https://app.netlify.com/projects/kubernetes-sigs-kueue/deploys/6a71b457ed04960008e80bbd

@kubernetes-prow kubernetes-prow Bot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Jul 31, 2026
@kubernetes-prow
kubernetes-prow Bot requested review from kshalot and mimowo July 31, 2026 14:34
@kubernetes-prow kubernetes-prow Bot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Jul 31, 2026
@kubernetes-prow

Copy link
Copy Markdown

Hi @andrewseif. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Tip

We noticed you've done this a few times! Consider joining the org to skip this step and gain /lgtm and other bot rights. We recommend asking approvers on your previous PRs to sponsor you.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

MultiKueue now tracks remote watcher goroutines, coordinates shutdown without holding the client mutex, and stops watch processing when contexts are canceled or result channels close. Tests cover watcher joining and cleanup.

Changes

MultiKueue watcher lifecycle

Layer / File(s) Summary
Watcher shutdown coordination
pkg/controller/admissionchecks/multikueue/multikueuecluster.go
remoteClient tracks watcher goroutines with a sync.WaitGroup. Watch processing handles events through a helper and exits on cancellation or result-channel closure. StopWatchers cancels the watch, clears cancellation state under the mutex, then waits for watchers.
Watcher lifecycle validation
pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go
Tests stop remote watchers after configuration, reconnect, and multi-cluster reconciliation cases. A regression test verifies that StopWatchers joins a parked watcher and returns.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: kshalot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: ensuring watcher goroutines stop before their owning object is released.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go`:
- Around line 697-702: Add table-driven Ginkgo/Gomega scenarios around the
watcher cleanup using controlled fake watchers and bounded or unbuffered
channels to cover blocking ResultChan(), queueWorkloadEvent() on wlUpdateCh, and
queueWatchEndedEvent() on watchEndedCh. For each boundary, invoke StopWatchers()
and assert it returns within a bounded timeout, preserving existing cleanup and
test behavior.

In `@pkg/controller/admissionchecks/multikueue/multikueuecluster.go`:
- Around line 422-424: Serialize watcher startup and shutdown in
updateConfigAndRefreshWatchers and StopWatchers: protect watchCancel access, the
watcher goroutine’s rc.watchers.Add(1) startup, and rc.watchers.Wait() with a
shared lifecycle barrier/lock. Ensure shutdown cannot complete while an overlap
callback is still able to register a watcher, while preserving existing
cancellation and watcher cleanup behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 367ae6b9-3bc1-4dcf-8b12-29aa6be5e729

📥 Commits

Reviewing files that changed from the base of the PR and between e88c9b0 and 4664068.

📒 Files selected for processing (2)
  • pkg/controller/admissionchecks/multikueue/multikueuecluster.go
  • pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go

Comment thread pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go
Comment thread pkg/controller/admissionchecks/multikueue/multikueuecluster.go Outdated
@olekzabl

olekzabl commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

/ok-to-test

@kubernetes-prow kubernetes-prow Bot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Aug 1, 2026
Comment on lines +422 to +424
rc.watchers.Add(1)
go func() {
defer rc.watchers.Done()

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.

On WaitGroup.Done, there's a comment:

Callers should prefer [WaitGroup.Go].

Which indeed seems nicely applicable in this case. WDYT?

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.

Done and it turns out this was blocking: verify-ci-lint was failing on exactly this (waitgroupgo: Goroutine creation can be simplified using WaitGroup.Go)

It folded neatly into CodeRabbit's concurrency point: the Add/Done pair became a single rc.watchers.Go(fn) inside a new runWatcher helper that also holds rc.mu, so starting a watcher and stopping the watchers are now mutually exclusive.

defer rc.watchers.Done()
log.V(2).Info("Starting watch")
for r := range newWatcher.ResultChan() {
resultCh := newWatcher.ResultChan()

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.

Nit: this var seems to have just 1 usage; maybe just inline?

resultCh := newWatcher.ResultChan()
watching:
for {
var r watch.Event

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.

Nit: I don't see a need for 2 vars r, ev.
Why not have case r, ok := <-resultCh and then do the whole handling (switch r.Type { ... }) inside that case?

@andrewseif andrewseif Aug 3, 2026 •

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.

Done. The switch r.Type now lives inside the receive case and the pre-declared var r watch.Event is gone. break watching still exits the labelled loop from inside the select, so the control flow is unchanged.

Comment on lines +598 to +601
select {
case rc.wlUpdateCh <- event.GenericEvent{Object: localWl}:
case <-ctx.Done():
}

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.

As you're securing rc.wlUpdateCh and rc.watchEndedCh in this way, why not also rc.cqUpdateCh?

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.

Done. My reasoning for leaving it was that queueEventsForLQ runs on the cache's informer goroutines rather than the ones StopWatchers joins, so it could not wedge the join. That is still true, but it is the same unbounded-block bug and leaving two of three guarded is worse than fixing all three a reader would rightly wonder which rule applies.

I added new test alongside the other two.

WDYT?

@olekzabl olekzabl Aug 3, 2026 •

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.

Hmm, actually, can you explain to me what's the purpose of guarding all these rc.xUpdateCh <- y with ctx.Done()?
Apologies, I'm only now realizing I don't get it. Because:

  1. If it's to prevent sending messages once ctx is done:
    It's speculative; the select semantics might still allow it if the channel buffer is not full.
  2. If it's to prevent blocking forever on a <- to a full channel (from which no-one will ever read):
    I don't see such a threat; all these rc.xUpdateCh are ultimately consumed by various controllers (via .WatchesRawSource(source.Channel(xUpdateCh, someHandler)))), which will still operate even when the watch context gets cancelled. So I don't see how blocking forever could happen.

Can you help me understand this? Or maybe are these guards all unnecessary?
(I'm not questioning the guard in establishWatcher, just the other ones).

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.

Good question, I added those guards thinking that it would prevent drainage, because of your comment, I actually went back and read through the controller-runtime properly.

My mental model was that these channels belong to the watch, so when the watch context was
cancelled I assumed the consumer side would go away with it and a send could be left hanging.

Your comment made me go back and verify it properly. I read source.Channel in vendor, and then
I removed the guards and re-ran the package suite under -race repeatedly to confirm nothing
regressed.

Reading it, syncLoop is the only reader of wlUpdateCh, watchEndedCh and cqUpdateCh, and
it runs on the manager's context rather than the watch context:

func (cs *channel[object, request]) syncLoop(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			cs.doStop()
			return
		case evt, stillOpen := <-cs.source:
			...
			cs.distribute(evt)
		}
	}
}

So cancelling a watch does not stop the drain at all. With the manager running those sends cannot block indefinitely.

Running it afterwards confirms nothing depended on them: the package suite is clean over 16 runs
of -race -count=3 with GORACE=log_path set, no failures and no race reports. The watch loop
keeps its ctx.Done() arm, since that is the one that actually fixes the race this PR is about.

you learn something new every day 😄

@kubernetes-prow kubernetes-prow Bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Aug 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/controller/admissionchecks/multikueue/multikueuecluster.go`:
- Around line 310-312: Synchronize the full remote-client refresh and cluster
removal/disconnection paths using the same lock: update setRemoteClientConfig,
stopAndRemoveCluster, and disconnectCluster so StopWatchers cannot clear
rc.watchCancel while updateConfigAndRefreshWatchers is still creating callbacks.
Preserve the connected state only after watcher callbacks are established,
preventing connState from remaining connected without running watchers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2886cf14-c3d3-4d49-8b7d-759e061b9bd3

📥 Commits

Reviewing files that changed from the base of the PR and between 4664068 and 9fdacb1.

📒 Files selected for processing (2)
  • pkg/controller/admissionchecks/multikueue/multikueuecluster.go
  • pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go

Comment on lines +310 to +312
var watchCancel context.CancelFunc
watchCtx, watchCancel = context.WithCancel(watchCtx)
rc.setWatchCancel(watchCancel)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== callers of updateConfigAndRefreshWatchers =="
rg -n -B5 -A5 'updateConfigAndRefreshWatchers\(' pkg/controller/admissionchecks/multikueue --glob '*.go'

echo "== callers of StopWatchers / stopAndRemoveCluster =="
rg -n -B5 -A5 '\.StopWatchers\(\)|stopAndRemoveCluster\(' pkg/controller/admissionchecks/multikueue --glob '*.go'

echo "== locking around remoteClients map / reconcile loop =="
rg -n -C6 'c\.lock\.(Lock|RLock)\(\)' pkg/controller/admissionchecks/multikueue --glob '*.go'

Repository: kubernetes-sigs/kueue

Length of output: 19226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file outline =="
ast-grep outline pkg/controller/admissionchecks/multikueue/multikueuecluster.go --match remoteClient --view expanded || true

echo "== relevant multikueuecluster.go sections =="
sed -n '240,380p' pkg/controller/admissionchecks/multikueue/multikueuecluster.go
echo "---"
sed -n '540,620p' pkg/controller/admissionchecks/multikueue/multikueuecluster.go
echo "---"
sed -n '740,875p' pkg/controller/admissionchecks/multikueue/multikueuecluster.go

echo "== remoteClient definitions and watchers-related methods =="
rg -n -C4 'type remoteClient|setWatchCancel|StopWatchers|runWatcher|startWatcherCallback|startWatcher|markConnected|isConnected|markDisconnected|updateConfigLock|connState|watchCancel|watchCtx' pkg/controller/admissionchecks/multikueue/multikueuecluster.go

echo "== tests around concurrent stop/watchers =="
rg -n -C8 'StopWatchersJoinsParkedWatcher|QueueEventSendsUnblockOnCancel|StopWatchers|mlock|updateConfigAndRefreshWatchers' pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go

Repository: kubernetes-sigs/kueue

Length of output: 29601


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Reconciler lock acquisition in reconcile flow =="
sed -n '850,895p' pkg/controller/admissionchecks/multikueue/multikueuecluster.go

echo "== Any reconcile loop lock around entire Reconcile =="
rg -n -C4 'c\.lock\.(Lock|Unlock|RLock|RUnlock)\(\)' pkg/controller/admissionchecks/multikueue/multikueuecluster.go | head -n 180

echo "== Test: Start/Reconcile/StopWatchers interleaving around updateConfigAndRefreshWatchers =="
rg -n -C10 'SetRemoteClientConfig|setRemoteClientConfig|hammerSetConfigWithReader|StopWatchersJoinsParkedWatcher|Concurrent|Reconcile' pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go | head -n 260

Repository: kubernetes-sigs/kueue

Length of output: 15239


Keep remote-client refresh and removal under the same synchronization.

setRemoteClientConfig() protects only updateConfigAndRefreshWatchers() via client.updateConfigLock, while stopAndRemoveCluster() and disconnectCluster() call rc.StopWatchers() after releasing c.lock. A deletion/disconnection can then clear rc.watchCancel while this refresh is still building the watch callbacks; each callback will skip runWatcher() once rc.watchCancel is nil, leaving connState marked connected with no watcher goroutines. Hold the same lock for the full refresh/removal path, or wait until watcher callbacks have started before calling markConnected().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/admissionchecks/multikueue/multikueuecluster.go` around lines
310 - 312, Synchronize the full remote-client refresh and cluster
removal/disconnection paths using the same lock: update setRemoteClientConfig,
stopAndRemoveCluster, and disconnectCluster so StopWatchers cannot clear
rc.watchCancel while updateConfigAndRefreshWatchers is still creating callbacks.
Preserve the connected state only after watcher callbacks are established,
preventing connState from remaining connected without running watchers.

@andrewseif

Copy link
Copy Markdown
Contributor Author

/test pull-kueue-test-e2e-multikueue-sequential-shard-0-main

default:
wlKeys, err := w.WorkloadKeysFor(r.Object)
if err != nil {
log.Error(err, "Cannot get workload keys", "jobKind", r.Object.GetObjectKind().GroupVersionKind())

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.

should we rather return error to bounce back with another reconcile?

@andrewseif andrewseif Aug 4, 2026 •

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 do not think so, but maybe I am wrong, there are mainly 3 reasons for this:

AFSIU, this runs inside the watcher goroutine rather than inside Reconcile, so there is no error return that would trigger a requeue, the only option would be to queue an event explicitly?

More importantly the failure is deterministic for the object in hand: every WorkloadKeysFor implementation fails either on a type assertion (not a workload, not a pod) or on a missing prebuilt-workload annotation. A retry would re-examine the same object and fail identically, so queueing a reconcile would spin rather than recover. And the watch event itself is not replayable, it has already been read off ResultChan(), and a reconcile does not redeliver it.

For context, this line is unchanged behaviour from main (multikueuecluster.go:440); it only moved in the diff because of the re-indent. If you would still like a reconcile queued on this path I am happy to do it.

Maybe a separate PR so it does not ride along with the race fix?

WDYT?

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.

For context, this line is unchanged behaviour from main (multikueuecluster.go:440); it only moved in the diff because of the re-indent. If you would still like a reconcile queued on this path I am happy to do it.

Ah, I see, let's keep it as is then and focus on the problem we have

default:
log.V(3).Info("Watch error with unexpected type", "type", fmt.Sprintf("%T", s))
resultCh := newWatcher.ResultChan()
watching:

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.

Using labels in code is notoriously ticky to read, except maybe some rare cases. I'm not sure this is one of them. The code is intended so much (8 levels), that I would prefer rather calling the code as a function.

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 didn't want to add another function, but now that I read this again, I think you are right 😅, let me put it in a function

@mimowo

mimowo commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

/approve
/lgtm
/cherrypick release-0.19
/cherrypick release-0.18
Thank you for hardening MultiKueue and fixing our tests

@k8s-infra-cherrypick-robot

Copy link
Copy Markdown
Contributor

@mimowo: once the present PR merges, I will cherry-pick it on top of release-0.18, release-0.19 in new PRs and assign them to you.

Details

In response to this:

/approve
/lgtm
/cherrypick release-0.19
/cherrypick release-0.18
Thank you for hardening MultiKueue and fixing our tests

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 4, 2026
@kubernetes-prow

Copy link
Copy Markdown

LGTM label has been added.

DetailsGit tree hash: aaab7c720f0e905978512c3d26c63f83cdbe9fcd

@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: andrewseif, mimowo

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

@kubernetes-prow kubernetes-prow Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 4, 2026
@kubernetes-prow
kubernetes-prow Bot merged commit 047967b into kubernetes-sigs:main Aug 4, 2026
56 checks passed
@kubernetes-prow kubernetes-prow Bot added this to the v0.20 milestone Aug 4, 2026
@k8s-infra-cherrypick-robot

Copy link
Copy Markdown
Contributor

@mimowo: new pull request created: #13829

Details

In response to this:

/approve
/lgtm
/cherrypick release-0.19
/cherrypick release-0.18
Thank you for hardening MultiKueue and fixing our tests

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-infra-cherrypick-robot

Copy link
Copy Markdown
Contributor

@mimowo: #13705 failed to apply on top of branch "release-0.18":

Applying: stop watcher goroutines from outliving their owner
Using index info to reconstruct a base tree...
M	pkg/controller/admissionchecks/multikueue/multikueuecluster.go
M	pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go
Falling back to patching base and 3-way merge...
Auto-merging pkg/controller/admissionchecks/multikueue/multikueuecluster.go
Auto-merging pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go
CONFLICT (content): Merge conflict in pkg/controller/admissionchecks/multikueue/multikueuecluster_test.go
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 stop watcher goroutines from outliving their owner

Details

In response to this:

/approve
/lgtm
/cherrypick release-0.19
/cherrypick release-0.18
Thank you for hardening MultiKueue and fixing our tests

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@mimowo

mimowo commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@andrewseif please prepare a CP for 0.18

@mimowo

mimowo commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

/release-note-edit

MultiKueue: Fixed an issue where remote-cluster watcher goroutines could continue running after a worker cluster was removed, disconnected, or reconfigured.

@kubernetes-prow kubernetes-prow Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed release-note-none Denotes a PR that doesn't merit a release note. labels Aug 4, 2026
kubernetes-prow Bot pushed a commit that referenced this pull request Aug 4, 2026
…ng their owner (#13839)

* stop watcher goroutines from outliving their owner

* apply comments

* apply comments and remove not needed guards
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. area/multikueue Issues or PRs related to MultiKueue cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/bug Categorizes issue or PR as related to a bug. kind/flake Categorizes issue or PR as related to a flaky test. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants