Repository navigation
fix(logging): Fix deadlock in deferred handler when creating loggers concurrently - #7632
Merged
Merged
Conversation
…instead of deadlocking the flush WithAttrs and WithGroup appended to the parent's children list under a read lock, so two components creating loggers at the same time could drop one child from the list. buildHandlers never reached that child, its buffered records were replayed by flushBuffer into addRecord, and addRecord waited on the lock flushBuffer holds. Alloy then hung in the logging block evaluation before any component started, about one restart in five. Take the write lock when registering a child, and let a child whose parent is already built derive its own handler on first use, so a missed child can no longer re-enter the buffer. Fixes grafana#7625
|
|
Contributor
|
This seems way to over-complicated to solve the linked issue. Checking using read lock first and then upgrading to write lock and recheck should be enough to fix this issue e.g. func (d *deferredSlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
d.mut.RLock()
if d.handle != nil {
defer d.mut.RUnlock()
return d.handle.WithAttrs(attrs)
}
d.mut.RUnlock()
d.mut.Lock()
defer d.mut.Unlock()
// buildHandlers may have run since the read lock was released.
if d.handle != nil {
return d.handle.WithAttrs(attrs)
}
child := &deferredSlogHandler{
attrs: attrs,
children: make([]*deferredSlogHandler, 0),
l: d.l,
}
d.children = append(d.children, child)
return child
}And then do the same for WithGroup |
kalleep
reviewed
Nov 4, 2026
Check for a built handler under the read lock first, then upgrade to the write lock and check again before appending the child, in both WithAttrs and WithGroup. Drop the parent pointer and the self-build path; with the append protected, buildHandlers reaches every child. The test now registers children concurrently before Update and checks that every one is built and replayed, instead of detaching a child by hand.
kalleep
reviewed
Nov 4, 2026
kalleep
approved these changes
Nov 4, 2026
jharvey10
pushed a commit
that referenced
this pull request
Nov 13, 2026
🤖 I have created a release *beep* *boop* --- <details><summary>1.22.0</summary> ## [1.22.0](v1.21.0...v1.22.0) (2026-11-12) ### ⚠ BREAKING CHANGES * **loki.enrich:** Removes `target_match_label`, `logs_match_label` and `metrics_match_label` from `loki.enrich` and `prometheus.enrich`. The `target_to_log_match` and `target_to_metric_match` arguments are now required. * **otelcol.connector:** This may render certain configurations invalid. These bad configurations were silently accepted before, but in practice they never caused problems because all connectors currently defined only output one signal type. This issue was surfaced while implementing #7385, which supports more than one output signal. ### Features 🌟 * Add k8seventreceiver to the OTel Engine ([#7425](#7425)) ([48317f3](48317f3)) * **component.http.oauth2:** Add jwt private key client authentication ([#7130](#7130)) ([6fbbee3](6fbbee3)) * **database_observability.sql_server:** Add `server_configuration` collector ([#7525](#7525)) ([9d18f83](9d18f83)) * **database_observability.sql_server:** Add `server_metrics` collector ([#7545](#7545)) ([f763183](f763183)) * **database_observability.sql_server:** Add additional performance counter metrics to server_metrics ([#7596](#7596)) ([a97d6a4](a97d6a4)) * **database_observability.sql_server:** Add throttling options to query_samples collector ([#7567](#7567)) ([8cfd0bd](8cfd0bd)) * **database_observability:** Add include settings for databases, schemas, and users ([#7642](#7642)) ([40be7ff](40be7ff)) * **database_observability:** Default `enable_pre_classified_wait_events` to true ([#7579](#7579)) ([21ac5ef](21ac5ef)) * **database_observability:** Emit parsed table names as a list with op=query_parsed_table_names_v2 ([#7520](#7520)) ([67aaa9d](67aaa9d)) * **database_observability:** Only enable MySQL perf_schema.eventsstatements collector by default in embedded prometheus exporter ([#7514](#7514)) ([d9e795f](d9e795f)) * **database_observability:** Preserve default exclusions and warn on settings mismatch ([#7626](#7626)) ([84a0021](84a0021)) * **helm:** Add distroless option ([#7516](#7516)) ([962af92](962af92)) * **loki.enrich:** Add multi-label matching ([#7053](#7053)) ([f168087](f168087)) * **loki.enrich:** Remove deprecated match arguments ([#7587](#7587)) ([fbd2a29](fbd2a29)) * **otelcol.connector.exceptions:** Add exceptions connector component from otel collector contrib ([#7385](#7385)) ([782d4e3](782d4e3)) * **otelcol.receiver.kafka:** Add the `dial_timeout` argument (default `10s`). Upstream now requires it. This also applies to `otelcol.exporter.kafka` ([#7586](#7586)) ([14ffd21](14ffd21)) * **otelcol.receiver.kafka:** Add the `max_decompress_batch_bytes` argument (default `1073741824`) ([#7586](#7586)) ([14ffd21](14ffd21)) * **otelcol.receiver.kafka:** Add the `max_prefetch` and `max_in_flight` blocks to `partition_processing` ([#7586](#7586)) ([14ffd21](14ffd21)) * **otelcol.receiver.kafka:** Deprecate `partition_processing > max_buffered_batches` in favor of `partition_processing > max_prefetch > batches`. Upstream removes it in v0.166.0 ([#7586](#7586)) ([14ffd21](14ffd21)) * **otelcol.receiver.kafka:** Support encoding extensions ([#7430](#7430)) ([ede6371](ede6371)) * **otelcol:** Add Azure encoding extension ([#7400](#7400)) ([146ed71](146ed71)) * **otelcol:** Upgrade to OTel Collector v0.164.1 ([#7586](#7586)) ([14ffd21](14ffd21)) * **prometheus.exporter.cadvisor:** Add housekeeping_interval arguments ([#7478](#7478)) ([163eeef](163eeef)) * **prometheus.operator:** Support Prometheus operator scrape classes ([#6466](#6466)) ([a941fb5](a941fb5)) * **prometheus.snmp_exporter:** Support multiple config files ([#6892](#6892)) ([51f6ffb](51f6ffb)) * Promote loki.enrich and prometheus.enrich to public preview ([#7597](#7597)) ([87cca81](87cca81)) * **pyroscope.ebpf:** Update ebpf-profiler fork to upstream v0.0.202640 ([#7408](#7408)) ([7b0591a](7b0591a)) * **pyroscope.java:** Update async-profiler to v4.6 ([#7447](#7447)) ([33bd346](33bd346)) ### Bug Fixes 🐛 * **collector:** Update root x/net dependency to v0.62.0 ([#7599](#7599)) ([5c7b2a1](5c7b2a1)) * **database_observability.mysql:** Enable additional collectors on embedded exporter ([#7608](#7608)) ([8caea29](8caea29)) * **database_observability.mysql:** Omit digest_text label ([#7607](#7607)) ([4ceaddf](4ceaddf)) * **database_observability.postgres:** Improve table registry lifecycle and unqualified lookup ([#7470](#7470)) ([587dab2](587dab2)) * **database_observability.postgres:** Include instance label in ID generation ([#7301](#7301)) ([295b6ce](295b6ce)) * **database_observability.sql_server:** Check server-level permissions and avoid false positives in health check ([#7656](#7656)) ([ae815a7](ae815a7)) * **database_observability.sql_server:** Include instance name in generated `server_id` ([#7521](#7521)) ([5e9571c](5e9571c)) * **database_observability.sql_server:** Parse TopSort and Spool wrappers and keep subtrees of unmodeled operators ([#7406](#7406)) ([2e887f6](2e887f6)) * **database_observability.sql_server:** Preserve query shape avoiding normalization ([#7395](#7395)) ([72bd5fc](72bd5fc)) * **database_observability.sql_server:** Report Azure SQL engine by name in engine_version ([#7502](#7502)) ([6760716](6760716)) * **database_observability.sql_server:** Support StmtCond explain plans ([#7598](#7598)) ([c320b9d](c320b9d)) * **database_observability.sql_server:** Validate parsed table names ([#7413](#7413)) ([7f06682](7f06682)) * **database_observability:** Apply exclusions to embedded exporters ([#7580](#7580)) ([8753e61](8753e61)) * **deps:** Update github.com/andybalholm/brotli to v1.2.7 ([#7519](#7519)) ([fccef0f](fccef0f)) * **faro.receiver:** Enforce download_from_origins on redirects and sourcemap URLs ([#7477](#7477)) ([9ddd772](9ddd772)) * Hide HTTP proxy env vars in support bundles ([#7559](#7559)) ([9a75d53](9a75d53)) * **logging:** Fix deadlock in deferred handler when creating loggers concurrently ([#7632](#7632)) ([27d88b9](27d88b9)) * **loki.process:** Move stage.logfmt to syntax.Validator ([#7446](#7446)) ([aba9a0e](aba9a0e)) * **loki.process:** Move stage.luhn to syntax.Validator ([#7421](#7421)) ([beeec13](beeec13)) * **loki.process:** Move stage.multiline to syntax.Validator ([#7420](#7420)) ([095f423](095f423)) * **loki.process:** Move stage.replace to syntax.Validator ([#7518](#7518)) ([2524277](2524277)) * **loki.source.cloudflare:** Stop additional_fields from corrupting the extended and all field presets ([#7535](#7535)) ([7fd4068](7fd4068)) * **loki.source.file:** Always update position ([#7641](#7641)) ([81c690f](81c690f)) * **loki.source.file:** Read recreated files when the inode is reused ([#7538](#7538)) ([e8a8e85](e8a8e85)) * **loki.source.kubernetes,loki.source.podlogs:** Prevent re-ingesting logs on re-tails ([#7507](#7507)) ([2db65de](2db65de)) * **loki.source.windowsevent:** Keep the saved bookmark when no new event arrives after a restart ([#7589](#7589)) ([b1cf13e](b1cf13e)) * **loki.write:** Keep the client running when its settings are unchanged ([#7620](#7620)) ([24064ae](24064ae)) * **loki.write:** WAL segment savepoints ([#7200](#7200)) ([2dcc043](2dcc043)) * Migrate config from type string to type secret [backport] ([#7499](#7499)) ([b89853a](b89853a)) * **mimir.alerts.kubernetes:** Pick up Secret changes without a restart ([#7273](#7273)) ([0bc2b79](0bc2b79)) * **mimir.rules.kubernetes:** Fix non-recovering unhealthy state after a failed restart when Mimir is unreachable ([#6339](#6339)) ([8dd633d](8dd633d)) * **otelcol.connector:** Reject output signals that a connector cannot produce ([#7389](#7389)) ([5343639](5343639)) * **otelcol.exporter.otlphttp:** Deprecate enabled in the cookies block ([#7418](#7418)) ([659c5eb](659c5eb)) * **otelcol.exporter.prometheus:** Pass missing `include_scope_labels` to converter ([#6787](#6787)) ([f0d65bd](f0d65bd)) * **otelcol.processor.tail_sampling:** Pass invert_match through for boolean_attribute policies ([#7444](#7444)) ([c139dd7](c139dd7)) * **otelcol:** Report full build version including pre-release suffix in service.version ([#7523](#7523)) ([e306584](e306584)) * **prometheus.exporter.kafka:** Avoid panic on empty consumer group member assignment ([#7428](#7428)) ([c49e0de](c49e0de)) * **prometheus.operator:** Initialize controller-runtime logging ([#7287](#7287)) ([072feda](072feda)) * **prometheus.remote_write:** Write start timestamp zero samples to the correct series ([#7633](#7633)) ([5a12f45](5a12f45)) * **prometheus:** Apply relabel and enrich to start timestamp zero samples ([#7647](#7647)) ([035e298](035e298)) * **pyroscope.ebpf:** Bug with dropped partially symbolized samples. ([#7409](#7409)) ([49b0764](49b0764)) * **remotecfg:** Restore last success metric in notModified response ([#7486](#7486)) ([6eaa407](6eaa407)) * **security/HIGH/internal/web/ui:** Update npm dependency vite to v7.3.7 [SECURITY] ([#7464](#7464)) ([3a126f4](3a126f4)) * Update go version to 1.27.3 ([#7469](#7469)) ([8d0105f](8d0105f)) </details> <details><summary>syntax: 0.1.3</summary> ## [0.1.3](syntax/v0.1.2...syntax/v0.1.3) (2026-11-12) ### Bug Fixes 🐛 * Update go version to 1.27.3 ([#7469](#7469)) ([8d0105f](8d0105f)) </details> <details><summary>helm-chart: 1.16.0</summary> ## [1.16.0](helm-chart/1.15.0...helm-chart/1.16.0) (2026-11-12) ### Features 🌟 * **helm:** Update to Grafana Alloy v1.22.0 </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: grafana-alloybot[bot] <167359181+grafana-alloybot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brief description of Pull Request
WithAttrsandWithGroupon the deferred slog handler appended to the parent's children list under a read lock, so two components creating loggers at the same time could drop one child from the list.buildHandlersnever reached that child; its buffered records were replayed byflushBufferintoaddRecord, which waits on the lockflushBufferholds, and Alloy hung in theloggingblock evaluation before any component started. The child registration now takes the write lock, and a child whose parent is already built derives its own handler on first use, so a missed child can no longer re-enter the buffer. Test:TestDeferredChildMissedByBuildIsBuiltFromParentdetaches a child, buffers a record on it, and checks thatUpdatereturns and the record is written with its attributes.Pull Request Details
Two layers on purpose: the write lock removes the lost-append race, and the self-build in
realHandlerkeeps the flush from deadlocking if any other path ever leaves a child unbuilt.Issue(s) fixed by this Pull Request
Fixes #7625
Notes to the Reviewer
PR Checklist