Handle null in LoadBalancer Request and Response (#1454)

* Handle null lbRequest and lbResponse values.

Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>

* Verify for null request on start.

Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>

---------

Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>
This commit is contained in:
Olga Maciaszek-Sharma
2025-01-22 18:41:55 +01:00
committed by GitHub
parent c50e97641f
commit ae1ac8b005
3 changed files with 51 additions and 12 deletions

View File

@@ -27,8 +27,10 @@ import io.micrometer.core.instrument.Tags;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.CompletionContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.RequestData;
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.ResponseData;
import org.springframework.util.StringUtils;
@@ -55,7 +57,11 @@ class LoadBalancerTags {
}
Iterable<Tag> buildSuccessRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
ServiceInstance serviceInstance = completionContext.getLoadBalancerResponse().getServer();
Response<ServiceInstance> lbResponse = completionContext.getLoadBalancerResponse();
if (lbResponse == null) {
return Tags.empty();
}
ServiceInstance serviceInstance = lbResponse.getServer();
Tags tags = Tags.of(buildServiceInstanceTags(serviceInstance));
Object clientResponse = completionContext.getClientResponse();
if (clientResponse instanceof ResponseData responseData) {
@@ -100,9 +106,9 @@ class LoadBalancerTags {
}
Iterable<Tag> buildDiscardedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
if (completionContext.getLoadBalancerRequest().getContext() instanceof RequestDataContext) {
RequestData requestData = ((RequestDataContext) completionContext.getLoadBalancerRequest().getContext())
.getClientRequest();
Request<Object> lbRequest = completionContext.getLoadBalancerRequest();
if (lbRequest != null && lbRequest.getContext() instanceof RequestDataContext requestDataContext) {
RequestData requestData = requestDataContext.getClientRequest();
if (requestData != null) {
return Tags.of(valueOrUnknown("method", requestData.getHttpMethod()),
valueOrUnknown("uri", getPath(requestData)), valueOrUnknown("serviceId", getHost(requestData)));
@@ -118,11 +124,15 @@ class LoadBalancerTags {
}
Iterable<Tag> buildFailedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
ServiceInstance serviceInstance = completionContext.getLoadBalancerResponse().getServer();
Response<ServiceInstance> lbResponse = completionContext.getLoadBalancerResponse();
if (lbResponse == null) {
return Tags.empty();
}
ServiceInstance serviceInstance = lbResponse.getServer();
Tags tags = Tags.of(buildServiceInstanceTags(serviceInstance)).and(exception(completionContext.getThrowable()));
if (completionContext.getLoadBalancerRequest().getContext() instanceof RequestDataContext) {
RequestData requestData = ((RequestDataContext) completionContext.getLoadBalancerRequest().getContext())
.getClientRequest();
Request<Object> lbRequest = completionContext.getLoadBalancerRequest();
if (lbRequest != null && lbRequest.getContext() instanceof RequestDataContext requestDataContext) {
RequestData requestData = requestDataContext.getClientRequest();
if (requestData != null) {
return tags.and(Tags.of(valueOrUnknown("method", requestData.getHttpMethod()),
valueOrUnknown("uri", getPath(requestData))));

View File

@@ -85,10 +85,10 @@ public class MicrometerStatsLoadBalancerLifecycle implements LoadBalancerLifecyc
@Override
public void onStartRequest(Request<Object> request, Response<ServiceInstance> lbResponse) {
if (request.getContext() instanceof TimedRequestContext) {
if (request != null && request.getContext() instanceof TimedRequestContext) {
((TimedRequestContext) request.getContext()).setRequestStartTime(System.nanoTime());
}
if (!lbResponse.hasServer()) {
if (lbResponse == null || !lbResponse.hasServer()) {
return;
}
ServiceInstance serviceInstance = lbResponse.getServer();
@@ -104,7 +104,11 @@ public class MicrometerStatsLoadBalancerLifecycle implements LoadBalancerLifecyc
@Override
public void onComplete(CompletionContext<Object, ServiceInstance, Object> completionContext) {
ServiceInstance serviceInstance = completionContext.getLoadBalancerResponse().getServer();
ServiceInstance serviceInstance = null;
Response<ServiceInstance> loadBalancerResponse = completionContext.getLoadBalancerResponse();
if (loadBalancerResponse != null) {
serviceInstance = loadBalancerResponse.getServer();
}
LoadBalancerProperties properties = serviceInstance != null
? loadBalancerFactory.getProperties(serviceInstance.getServiceId())
: loadBalancerFactory.getProperties(null);
@@ -121,7 +125,11 @@ public class MicrometerStatsLoadBalancerLifecycle implements LoadBalancerLifecyc
if (activeRequestsCounter != null) {
activeRequestsCounter.decrementAndGet();
}
Object loadBalancerRequestContext = completionContext.getLoadBalancerRequest().getContext();
Request<Object> lbRequest = completionContext.getLoadBalancerRequest();
if (lbRequest == null) {
return;
}
Object loadBalancerRequestContext = lbRequest.getContext();
if (requestHasBeenTimed(loadBalancerRequestContext)) {
if (CompletionContext.Status.FAILED.equals(completionContext.status())) {
Timer.builder("loadbalancer.requests.failed")

View File

@@ -47,6 +47,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMapAdapter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.loadbalancer.stats.LoadBalancerTags.UNKNOWN;
@@ -243,6 +244,26 @@ class MicrometerStatsLoadBalancerLifecycleTests {
Tag.of("serviceInstance.port", "0"), Tag.of("status", "200"), Tag.of("uri", UNKNOWN));
}
@Test
void shouldHandleNullLoadBalancerResponse() {
RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(),
new HttpHeaders(), new HashMap<>());
Request<Object> lbRequest = new DefaultRequest<>(new RequestDataContext(requestData));
assertThatCode(() -> {
statsLifecycle.onStartRequest(lbRequest, null);
statsLifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, null));
}).doesNotThrowAnyException();
}
@Test
void shouldHandleNullLoadBalancerRequest() {
Response<ServiceInstance> lbResponse = new EmptyResponse();
assertThatCode(() -> {
statsLifecycle.onStartRequest(null, lbResponse);
statsLifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, null, lbResponse));
}).doesNotThrowAnyException();
}
private static class StatsTestContext {
}