fix: make AddFixSuggestion thread-safe#183
Conversation
Agent-Logs-Url: https://github.com/microsoft/azure-linux-dev-tools/sessions/5c16317f-6885-4df7-8091-874f9b19917c Co-authored-by: dmcilvaney <23200982+dmcilvaney@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/azure-linux-dev-tools/sessions/5c16317f-6885-4df7-8091-874f9b19917c Co-authored-by: dmcilvaney <23200982+dmcilvaney@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/azure-linux-dev-tools/sessions/5c16317f-6885-4df7-8091-874f9b19917c Co-authored-by: dmcilvaney <23200982+dmcilvaney@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/azure-linux-dev-tools/sessions/5c16317f-6885-4df7-8091-874f9b19917c Co-authored-by: dmcilvaney <23200982+dmcilvaney@users.noreply.github.com>
|
@copilot What drove the decision to have a new type rather than just adding a dedicated mutex to env that protects the hints? Pros/Cons of each? |
The main driver was Pros of the current approach: safe with |
| state.suggestions = append(state.suggestions, suggestion) | ||
| } | ||
|
|
||
| func (state *fixSuggestionState) Snapshot() []string { |
There was a problem hiding this comment.
@copilot Can we use a more simple name here?
Also, please add brief comments to these since they are public.
There was a problem hiding this comment.
Updated in 38f5a58: renamed Snapshot() to All() and added brief doc comments to the helper methods.
Agent-Logs-Url: https://github.com/microsoft/azure-linux-dev-tools/sessions/74c9103d-8308-4e14-a8f4-abbb5d53d3fc Co-authored-by: dmcilvaney <23200982+dmcilvaney@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
This PR addresses a concurrency hazard in azldev.Env fix-suggestion tracking by replacing the unsynchronized []string accumulator with a shared, mutex-guarded state that remains shared across WithCancel()-derived child envs. It also updates the printing path to snapshot suggestions before iterating, preventing concurrent mutation while formatting/logging.
Changes:
- Replace
Env.fixSuggestions []stringwith a shared*fixSuggestionStateguarded bysync.Mutexto makeAddFixSuggestionsafe under concurrent use. - Update
PrintFixSuggestionsto read a snapshot (All()) before iterating/logging. - Add regression tests covering concurrent
AddFixSuggestionand additions fromWithCancel()child envs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| internal/app/azldev/env.go | Introduces mutex-guarded shared fix-suggestion storage and snapshot-based printing to prevent data races. |
| internal/app/azldev/env_test.go | Adds concurrency regression tests validating thread-safe suggestion collection and parent visibility from child envs. |
AddFixSuggestioncould be called from multiple goroutines, butEnvstored suggestions in an unsynchronized slice. This change makes fix suggestion collection safe under concurrent use and preserves suggestions added throughWithCancel()child envs.Shared synchronized storage
[]stringonEnvwith shared fix-suggestion state guarded by a mutex.Envcopies so child envs append into the same suggestion list.Safe read path
Focused regression coverage
AddFixSuggestioncalls on the same env.WithCancel()child envs to verify suggestions remain visible on the parent.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
dario.cat/home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses /home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses report --ignore golang.org/x --ignore github.com/microsoft/azure-linux-dev-tools ./... ux-amd64/pkg/tool/linux_amd64/vet iter.go -ifaceassert l/linux_amd64/vet ux-amd64/pkg/tool/linux_amd64/vet -o /tmp/go-build2551339997/b802/_pkg_.a -trimpath ux-amd64/pkg/tool/linux_amd64/vet -p main -lang=go1.25 ux-amd64/pkg/tool/linux_amd64/vet(dns block)example.com/tmp/go-build329767517/b669/sourceproviders.test /tmp/go-build329767517/b669/sourceproviders.test -test.testlogfile=/tmp/go-build329767517/b669/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build329767517/b558/vet.cfg nal/app/azldev/c-errorsas --pretty=format:-ifaceassert ux-amd64/pkg/tool/linux_amd64/vet -- /home/REDACTED/wor-atomic ux-amd64/pkg/too-bool ToU22LcjRrvL log -dev-tools/magef-errorsas %cd ux-amd64/pkg/tool/linux_amd64/vet -- k/azure-linux-de-atomic ux-amd64/pkg/too-bool ux-amd64/pkg/too-buildtags(dns block)/tmp/go-build2950161913/b669/sourceproviders.test /tmp/go-build2950161913/b669/sourceproviders.test -test.testlogfile=/tmp/go-build2950161913/b669/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build2950161913/b510/vet.cfg v-tools/azure-linux-dev-tools/sc-- "||"}} ux-amd64/bin/go GOINSECURE GOMOD GOMODCACHE 9gZq2KlM9N4Y est.�� mds/config GO111MODULE e1778a632bfd545a839f5e2e1069c0a7-d/mockgen GOINSECURE GOMOD nk e1778a632bfd545a-buildtags(dns block)/tmp/go-build103592675/b669/sourceproviders.test /tmp/go-build103592675/b669/sourceproviders.test -test.testlogfile=/tmp/go-build103592675/b669/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build103592675/b412/vet.cfg v-tools/azure-linux-dev-tools/in-- d656f853378c1fc3--bundle ux-amd64/bin/go y ternal/app/azlde-V=full nk ortcfg mod estenv nk ux-amd64/bin/go(dns block)go.opentelemetry.io/home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses /home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses report --ignore golang.org/x --ignore github.com/microsoft/azure-linux-dev-tools ./... ux-amd64/pkg/tool/linux_amd64/vet iter.go -ifaceassert l/linux_amd64/vet ux-amd64/pkg/tool/linux_amd64/vet -o /tmp/go-build2551339997/b802/_pkg_.a -trimpath ux-amd64/pkg/tool/linux_amd64/vet -p main -lang=go1.25 ux-amd64/pkg/tool/linux_amd64/vet(dns block)go.szostok.io/home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses /home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses report --ignore golang.org/x --ignore github.com/microsoft/azure-linux-dev-tools ./... ux-amd64/pkg/tool/linux_amd64/vet iter.go -ifaceassert l/linux_amd64/vet ux-amd64/pkg/tool/linux_amd64/vet -o /tmp/go-build2551339997/b802/_pkg_.a -trimpath ux-amd64/pkg/tool/linux_amd64/vet -p main -lang=go1.25 ux-amd64/pkg/tool/linux_amd64/vet(dns block)go.uber.org/home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses /home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses report --ignore golang.org/x --ignore github.com/microsoft/azure-linux-dev-tools ./... ux-amd64/pkg/tool/linux_amd64/vet iter.go -ifaceassert l/linux_amd64/vet ux-amd64/pkg/tool/linux_amd64/vet -o /tmp/go-build2551339997/b802/_pkg_.a -trimpath ux-amd64/pkg/tool/linux_amd64/vet -p main -lang=go1.25 ux-amd64/pkg/tool/linux_amd64/vet(dns block)go.yaml.in/home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses /home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses report --ignore golang.org/x --ignore github.com/microsoft/azure-linux-dev-tools ./... ux-amd64/pkg/tool/linux_amd64/vet iter.go -ifaceassert l/linux_amd64/vet ux-amd64/pkg/tool/linux_amd64/vet -o /tmp/go-build2551339997/b802/_pkg_.a -trimpath ux-amd64/pkg/tool/linux_amd64/vet -p main -lang=go1.25 ux-amd64/pkg/tool/linux_amd64/vet(dns block)gopkg.in/home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses /home/REDACTED/.cache/go-build/7b/7be0cdfd4e5e66df21672375347bd23e46040e0c601b55d1ed8bef64f03acd06-d/go-licenses report --ignore golang.org/x --ignore github.com/microsoft/azure-linux-dev-tools ./... ux-amd64/pkg/tool/linux_amd64/vet iter.go -ifaceassert l/linux_amd64/vet ux-amd64/pkg/tool/linux_amd64/vet -o /tmp/go-build2551339997/b802/_pkg_.a -trimpath ux-amd64/pkg/tool/linux_amd64/vet -p main -lang=go1.25 ux-amd64/pkg/tool/linux_amd64/vet(dns block)invalid-domain.there.is.no.such.resource/tmp/go-build329767517/b662/rpmprovider.test /tmp/go-build329767517/b662/rpmprovider.test -test.testlogfile=/tmp/go-build329767517/b662/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build329767517/b549/vet.cfg nal/utils/parmap-errorsas --pretty=format:-ifaceassert ux-amd64/pkg/tool/linux_amd64/vet -- /home/REDACTED/wor-atomic ux-amd64/pkg/too-bool ux-amd64/pkg/too-buildtags tbui�� -dev-tools/inter-errorsas GO111MODULE ux-amd64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE ux-amd64/pkg/too-buildtags(dns block)If you need me to access, download, or install something from one of these locations, you can either: