From 7a04708c41f54fe6f2307e3bd607b9bb7c103090 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 21 Jun 2017 14:19:10 -0700 Subject: [PATCH] Make sure the HealthMvcEndpoint is thread-safe Previously, HealthMvcEndpoint stored the cached Health and its last access time in two separate fields. Neither field was volatile and no synchronization was used. This meant that there were potential visibility problems. In a possible worst case scenario one field may see the updated access time but an old health so it would incorrectly believe that the old health was up-to-date and return it. This commit reworks the endpoint to store the cached health and the time at which it was created in a single, volatile field. This ensures that the cached health and its creation time will be visible across threads. Note that a race between threads when the cache is stale is still possible. This race may result in multiple calls to the delegate but these should be harmless. Closes gh-9454 --- .../endpoint/mvc/HealthMvcEndpoint.java | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java index 9ebaf80d24e..48b73d2a817 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java @@ -60,14 +60,12 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter roles; + private volatile CachedHealth cachedHealth; + private Map statusMapping = new HashMap(); private RelaxedPropertyResolver securityPropertyResolver; - private long lastAccess = 0; - - private Health cached; - public HealthMvcEndpoint(HealthEndpoint delegate) { this(delegate, true); } @@ -165,22 +163,29 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter= getDelegate().getTimeToLive(); + return (accessTime - cachedHealth.creationTime) >= getDelegate().getTimeToLive(); } protected boolean exposeHealthDetails(HttpServletRequest request, @@ -221,4 +226,21 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter