Stop one bad property getter from killing the whole capabilities snapshot (GH-3740) - #3741
Merged
Merged
Conversation
…shot (GH-3740) AzureServiceBusTransport.HostName parsed the host out of ConnectionString!, so it threw a NullReferenceException for every credential-based connection (FullyQualifiedNamespace + TokenCredential / NamedKeyCredential / SasCredential) -- the normal managed-identity setup, where there is no connection string at all. HostName is public, BrokerDescription reads every public property reflectively, and ServiceCapabilities.ReadFrom was all-or-nothing, so that single getter cost the service its entire capabilities snapshot. CritterWatch retries the same doomed read on every batch, so the service heartbeats forever and never completes a handshake. - HostName returns the connection-string host when there is one, otherwise FullyQualifiedNamespace (which *is* the host name for credential-based connections), and null only when nothing is configured. Also fixes the MassTransit interop Uris, which interpolate it. The Endpoint parse now splits on the first '=' only, since SharedAccessKey values carry base64 padding. - [IgnoreDescription] on BusClient / ManagementClient: reading those lazily builds a LIVE Azure client as a side effect of describing configuration, and throws outright when nothing is configured. NATS and Pulsar already mark their live-runtime properties this way. - [IgnoreDescription] on PulsarTransport's this[Uri] indexer -- reflection counts an indexer as a property and GetValue without index arguments throws TargetParameterCountException, which was enough to break the snapshot for every application using Pulsar. - ServiceCapabilities.ReadFrom is now best effort per section, and per transport and per endpoint within the reflective sections: a failure is logged and skipped rather than aborting everything. Cancellation still propagates instead of being logged as a section failure. Companion hardening in JasperFx (so a throwing getter degrades to a "could not be read" value) is JasperFx/jasperfx#590. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RainvDHdde5JUAiGEPEgWw
This was referenced Jul 31, 2026
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.
Closes #3740.
From a user error report: an Azure Service Bus service on 6.24.0 / JasperFx 2.36.2 could never hand its capabilities to CritterWatch.
HostNameparsed the host out ofConnectionString!, so it threw for every credential-based connection (FullyQualifiedNamespace+TokenCredential/AzureNamedKeyCredential/AzureSasCredential) — the normal managed-identity setup, where there is no connection string at all.HostNameis public,BrokerDescriptionreads every public property reflectively, andServiceCapabilities.ReadFromwas all-or-nothing, so that one getter cost the service its whole snapshot. CritterWatch retries the same doomed read on every batch, so the service heartbeats forever and never completes a handshake — no endpoints, no message types, no stores.What changed
Azure Service Bus
HostNamereturns the connection-string host when there is one, otherwiseFullyQualifiedNamespace(which is the host name for credential-based connections), and null only when nothing is configured at all. This also fixes theIMassTransitInteropEndpoint.MassTransitUri()implementations for queues, topics and subscriptions, which interpolate it. TheEndpoint=parse now splits on the first=only, sinceSharedAccessKeyvalues carry base64 padding.[IgnoreDescription]onBusClient/ManagementClient: reading those lazily builds a live Azure client as a side effect of describing configuration, and throwsArgumentNullExceptionoutright when nothing is configured yet. NATS and Pulsar already mark their live-runtime properties this way.Pulsar
[IgnoreDescription]onthis[Uri]. Reflection counts an indexer as a property andGetValuewithout index arguments throwsTargetParameterCountException— enough to break the snapshot for every application using Pulsar.ServiceCapabilities
ReadFromis best effort per section, and per transport and per endpoint inside the two reflective sections: a failure is logged (with the section / protocol / endpoint Uri) and skipped instead of aborting everything. A capabilities snapshot is diagnostic data — losing one section beats losing all of it.Tests
CoreTests/Acceptance/service_capabilities_resilience_3740.cs— a transport with a throwing getter is skipped while a well-behaved one still lands, plus the cancellation case.Wolverine.AzureServiceBus.Tests/broker_description_with_credentials_3740.cs—HostNamefor token-credential, connection-string, no-Endpoint-segment and nothing-configured setups, and thatBrokerDescriptionbuilds cleanly for a credential-based transport.Full
CoreTests(2148) and the ASB unit tests (35) green locally.Companion hardening in JasperFx, so a throwing getter degrades to a "could not be read" value instead of an exception: JasperFx/jasperfx#591 (JasperFx/jasperfx#590).
🤖 Generated with Claude Code
https://claude.ai/code/session_01RainvDHdde5JUAiGEPEgWw