Skip to content

[FEATURE REQ] Provide reactive health indicators for Spring Cloud Azure #36309

Open
@paul-kraftlauget

Description

Describe the bug

When using a WebFlux application, health indicators should extend org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator and avoid calling Mono.block().

Furthermore, even if refactored, blockhound reports some blocking calls during the health check that need to be removed (for example, StorageBlobHealthIndicator, StorageQueueHealthIndicator, see stacktrace below).

Exception or Stack Trace

Original Stack Trace:
Caused by: reactor.blockhound.BlockingOperationError: Blocking call! java.io.RandomAccessFile#readBytes
	at java.base/java.io.RandomAccessFile.readBytes(RandomAccessFile.java)
	Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: 
Assembly trace from producer [reactor.core.publisher.MonoHandle] :
	reactor.core.publisher.Mono.mapNotNull
	com.azure.core.implementation.http.rest.AsyncRestProxy.handleBodyReturnType(AsyncRestProxy.java:200)
Error has been observed at the following site(s):
	*_______Mono.mapNotNull ⇢ at com.azure.core.implementation.http.rest.AsyncRestProxy.handleBodyReturnType(AsyncRestProxy.java:200)
	|_             Mono.map ⇢ at com.azure.core.implementation.http.rest.AsyncRestProxy.handleRestResponseReturnType(AsyncRestProxy.java:148)
	|_   Mono.switchIfEmpty ⇢ at com.azure.core.implementation.http.rest.AsyncRestProxy.handleRestResponseReturnType(AsyncRestProxy.java:149)
	|_                      ⇢ at com.azure.core.implementation.http.rest.AsyncRestProxy.lambda$handleRestReturnType$9(AsyncRestProxy.java:230)
	*__________Mono.flatMap ⇢ at com.azure.core.implementation.http.rest.AsyncRestProxy.handleRestReturnType(AsyncRestProxy.java:229)
	|_                      ⇢ at com.azure.storage.queue.implementation.ServicesImpl.getPropertiesWithResponseAsync(ServicesImpl.java:444)
	|_             Mono.map ⇢ at com.azure.storage.queue.QueueServiceAsyncClient.getPropertiesWithResponse(QueueServiceAsyncClient.java:428)
	|_                      ⇢ at com.azure.core.util.FluxUtil.lambda$withContext$9(FluxUtil.java:401)
	*__Mono.deferContextual ⇢ at com.azure.core.util.FluxUtil.withContext(FluxUtil.java:389)
	|_                      ⇢ at com.azure.core.util.FluxUtil.withContext(FluxUtil.java:370)
	|_                      ⇢ at com.azure.storage.queue.QueueServiceAsyncClient.getPropertiesWithResponse(QueueServiceAsyncClient.java:419)
...
Original Stack Trace:
		at java.base/java.io.RandomAccessFile.readBytes(RandomAccessFile.java)
		at java.base/java.io.RandomAccessFile.read(RandomAccessFile.java:405)
		at java.base/java.io.RandomAccessFile.readFully(RandomAccessFile.java:469)
		at java.base/java.util.zip.ZipFile$Source.readFullyAt(ZipFile.java:1348)
		at java.base/java.util.zip.ZipFile$ZipFileInputStream.initDataOffset(ZipFile.java:915)
		at java.base/java.util.zip.ZipFile$ZipFileInputStream.read(ZipFile.java:931)
		at java.base/java.util.zip.ZipFile$ZipFileInflaterInputStream.fill(ZipFile.java:448)
		at java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
		at java.base/java.io.InputStream.readNBytes(InputStream.java:506)
		at java.base/java.util.jar.JarFile.getBytes(JarFile.java:812)
		at java.base/java.util.jar.JarFile.checkForSpecialAttributes(JarFile.java:1002)
		at java.base/java.util.jar.JarFile.isMultiRelease(JarFile.java:389)
		at java.base/java.util.jar.JarFile.getEntry(JarFile.java:511)
		at java.base/sun.net.www.protocol.jar.URLJarFile.getEntry(URLJarFile.java:131)
		at java.base/sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:135)
		at java.base/sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:175)
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.parse(ServiceLoader.java:1172)
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass(ServiceLoader.java:1213)
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1228)
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273)
		at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309)
		at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393)
		at java.xml/javax.xml.stream.FactoryFinder$1.run(FactoryFinder.java:350)
		at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
		at java.xml/javax.xml.stream.FactoryFinder.findServiceProvider(FactoryFinder.java:339)
		at java.xml/javax.xml.stream.FactoryFinder.find(FactoryFinder.java:310)
		at java.xml/javax.xml.stream.XMLInputFactory.newFactory(XMLInputFactory.java:288)
		at com.fasterxml.jackson.dataformat.xml.util.StaxUtil.defaultInputFactory(StaxUtil.java:128)
		at com.fasterxml.jackson.dataformat.xml.XmlFactory.<init>(XmlFactory.java:123)
		at com.fasterxml.jackson.dataformat.xml.XmlFactory.<init>(XmlFactory.java:110)
		at com.fasterxml.jackson.dataformat.xml.XmlFactory.<init>(XmlFactory.java:103)
		at com.fasterxml.jackson.dataformat.xml.XmlFactory.<init>(XmlFactory.java:87)
		at com.fasterxml.jackson.dataformat.xml.XmlMapper.<init>(XmlMapper.java:135)
		at com.fasterxml.jackson.dataformat.xml.XmlMapper.builder(XmlMapper.java:226)
		at com.azure.core.implementation.jackson.XmlMapperFactory.createXmlMapper(XmlMapperFactory.java:88)
		at com.azure.core.implementation.jackson.ObjectMapperFactory.createXmlMapper(ObjectMapperFactory.java:42)
		at com.azure.core.implementation.jackson.ObjectMapperShim.createXmlMapper(ObjectMapperShim.java:78)
		at com.azure.core.util.serializer.JacksonAdapter$GlobalXmlMapper.<clinit>(JacksonAdapter.java:59)
		at com.azure.core.util.serializer.JacksonAdapter.getXmlMapper(JacksonAdapter.java:462)
		at com.azure.core.util.serializer.JacksonAdapter.lambda$deserialize$8(JacksonAdapter.java:343)
		at com.azure.core.util.serializer.JacksonAdapter.useAccessHelper(JacksonAdapter.java:483)
		at com.azure.core.util.serializer.JacksonAdapter.deserialize(JacksonAdapter.java:338)
		at com.azure.core.implementation.serializer.HttpResponseBodyDecoder.deserialize(HttpResponseBodyDecoder.java:166)
		at com.azure.core.implementation.serializer.HttpResponseBodyDecoder.deserializeBody(HttpResponseBodyDecoder.java:139)
		at com.azure.core.implementation.serializer.HttpResponseBodyDecoder.decodeByteArray(HttpResponseBodyDecoder.java:82)
		at com.azure.core.implementation.serializer.HttpResponseDecoder$HttpDecodedResponse.getDecodedBody(HttpResponseDecoder.java:93)
		at reactor.core.publisher.Mono.lambda$mapNotNull$25(Mono.java:3432)
		at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:113)

To Reproduce

Steps to reproduce the behavior:

Change health indicators to extend org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator and add blockhound library to test classpath to discover blocking invocation.

Expected behavior

The Azure SDK should automatically discover a WebFlux application and initialize reactive health indicators for all health indicators. An application using both Azure SDK and blockhound should not fail on azure SDK's blocking implementations (for example, load classes containing blocking calls during spring startup and not during health indicator execution).

Metadata

Assignees

Labels

Azure.Coreazure-coreClientThis issue points to a problem in the data-plane of the library.customer-reportedIssues that are reported by GitHub users external to the Azure organization.feature-requestThis issue requires a new behavior in the product in order be resolved.needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK team

Type

No type

Projects

  • Status

    No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions