Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delay before we announce service as healthy. #46

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.15.0] - 2024-10-30

### Added

* Environment specific configuration.
Test environment has now configured automatically to have fast shutdown.
* Delay before we announce service as healthy.

## [2.14.5] - 2024-07-16

### Added
Expand Down
5 changes: 2 additions & 3 deletions build.common.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ group = 'com.transferwise.common'
apply from: "${project.rootDir}/build.libraries.gradle"

configurations {
all {
configureEach {
exclude module: 'slf4j-log4j12'

resolutionStrategy {
Expand Down Expand Up @@ -90,7 +90,7 @@ test {
useJUnitPlatform()
}

tasks.withType(Checkstyle) {
tasks.withType(Checkstyle).configureEach {
config = resources.text.fromFile(file('../google_checks.xml'))
maxWarnings = 0

Expand All @@ -101,7 +101,6 @@ tasks.withType(Checkstyle) {
}

tasks.findAll { it.name.startsWith("spotbugs") }*.configure {
effort = "max"
excludeFilter = file('../spotbugs-exclude.xml')

reports {
Expand Down
15 changes: 11 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import org.eclipse.jgit.api.errors.RefAlreadyExistsException
import com.github.spotbugs.snom.Confidence
import com.github.spotbugs.snom.Effort

plugins {
id "com.github.spotbugs" version "5.0.14" apply false
id "com.github.spotbugs" version "6.0.+"
id "idea"
id 'org.ajoberstar.grgit' version '5.2.0'
id 'io.github.gradle-nexus.publish-plugin' version "1.1.0"
id 'org.ajoberstar.grgit' version '5.3.0'
id 'io.github.gradle-nexus.publish-plugin' version "2.0.0"
}

idea.project {
Expand Down Expand Up @@ -37,4 +39,9 @@ nexusPublishing {
password = System.getenv("SONATYPE_PASSWORD")
}
}
}
}

spotbugs {
effort = Effort.valueOf('MAX')
reportLevel = Confidence.valueOf('DEFAULT')
}
5 changes: 3 additions & 2 deletions build.libraries.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ext {

libraries = [
// version defined
awaitility : 'org.awaitility:awaitility:4.2.0',
awaitility : 'org.awaitility:awaitility:4.2.2',
dbScheduler : 'com.github.kagkarlsson:db-scheduler:12.1.0',
dbSchedulerSpringBootStarter : 'com.github.kagkarlsson:db-scheduler-spring-boot-starter:12.1.0',
guava : 'com.google.guava:guava:33.0.0-jre',
Expand All @@ -13,7 +13,8 @@ ext {
jakartaServletApi : 'jakarta.servlet:jakarta.servlet-api:6.0.0',
spotbugsAnnotations : "com.github.spotbugs:spotbugs-annotations:${spotbugs.toolVersion.get()}",
springBootDependencies : "org.springframework.boot:spring-boot-dependencies:${springBootVersion}",
twBaseUtils : 'com.transferwise.common:tw-base-utils:1.12.4',
twBaseUtils : 'com.transferwise.common:tw-base-utils:1.13.0',
wiseEnvironmentStarter : "com.transferwise.common:wise-environment-starter:0.0.3",

// versions managed by spring-boot-dependencies platform
flywayCore : 'org.flywaydb:flyway-core',
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dependencies {
implementation libraries.springWeb
implementation libraries.reactorCore
implementation libraries.guava
implementation libraries.wiseEnvironmentStarter

compileOnly libraries.dbSchedulerSpringBootStarter
compileOnly libraries.springContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public class GracefulShutdownHealthIndicator extends AbstractHealthIndicator {

/*
* We need to access the registry in a lazy way, otherwise we may easily run into circular dependencies with other strategies.
*
*
* E.g. some other strategy may inject something needing actuators, which in turn triggers creating the health indicators.
*
*
* We could avoid this by for example sending out the list of all strategies in some callback method, e.g. "applicationStarted",
* but that kind of solution seems a bit of over-engineering.
*/
Expand All @@ -28,15 +28,15 @@ public class GracefulShutdownHealthIndicator extends AbstractHealthIndicator {

private RateLimiter logsRateLimiter = RateLimiter.create(0.2);

private boolean ready;
private volatile boolean ready;

@Override
protected void doHealthCheck(Health.Builder builder) {
if (!ready) {
for (GracefulShutdownStrategy strategy : registry.getStrategies()) {
if (!strategy.isReady()) {
if (logsRateLimiter.tryAcquire()) {
log.info("" + strategy + " is not ready. Not considering application ready yet.");
log.info("{} is not ready. Not considering application ready yet.", strategy);
}
builder.down();
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.transferwise.common.gracefulshutdown.config;

import com.wise.common.environment.WiseEnvironment;
import com.wise.common.environment.WiseProfile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;

@Slf4j
public class GracefulShutdownEnvironmentPostProcessor implements EnvironmentPostProcessor {

@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

WiseEnvironment.setDefaultProperties(dsl -> dsl
.source("tw-graceful-shutdown")

.keyPrefix("tw-graceful-shutdown.")
.profile(WiseProfile.WISE)
.set("startupHealthyDelayMs", 0)

.keyPrefix("tw-graceful-shutdown.")
.profile(WiseProfile.PRODUCTION)
.set("startupHealthyDelayMs", 7500)

.keyPrefix("tw-graceful-shutdown.")
.profile(WiseProfile.TEST)
.set("startupHealthyDelayMs", 1)
.set("shutdown-timeout-ms", 1)
.set("clients-reaction-time-ms", 1)
.set("strategies-check-interval-time-ms", 5)
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public static class FlagProperty {

private int resourceCheckIntervalTimeMs = 250;

// Usually services start tons of background tasks, which can overwhelm JIT queues and class loadings.
// We would create artificial delay, before we allow latency sensitive REST calls in.
private int startupHealthyDelayMs = 0;

private FlagProperty healthIndicator;
private FlagProperty requestCountStrategy;
private FlagProperty kagkarlssonDbScheduler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.transferwise.common.gracefulshutdown.GracefulShutdownIgnore;
import com.transferwise.common.gracefulshutdown.GracefulShutdownStrategy;
import com.transferwise.common.gracefulshutdown.config.GracefulShutdownProperties;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -23,6 +24,7 @@
@Slf4j
public abstract class BaseReactiveResourceShutdownStrategy<T> implements GracefulShutdownStrategy {

@SuppressFBWarnings("CT")
public BaseReactiveResourceShutdownStrategy(@NonNull Class<T> resourceType, @NonNull ApplicationContext applicationContext,
@NonNull GracefulShutdownProperties gracefulShutdownProperties) {
this.resourceType = resourceType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.transferwise.common.gracefulshutdown.strategies;

import com.transferwise.common.gracefulshutdown.GracefulShutdownStrategy;
import com.transferwise.common.gracefulshutdown.config.GracefulShutdownProperties;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

@Slf4j
public class GracefulShutdownHealthStrategy implements GracefulShutdownStrategy {
Expand All @@ -11,13 +14,21 @@ public class GracefulShutdownHealthStrategy implements GracefulShutdownStrategy
private volatile boolean shutdownInProgress;

@Getter
private volatile boolean startupInProgress = true;
private volatile Long startupTime = null;

private AtomicBoolean readyDeclared = new AtomicBoolean(false);

@Autowired
private GracefulShutdownProperties gracefulShutdownProperties;

@Override
public void applicationStarted() {
log.info("Considering Service as fully started. Starting to broadcast UP state.");
startupInProgress = false;
startupTime = System.currentTimeMillis();
shutdownInProgress = false;

if (gracefulShutdownProperties.getStartupHealthyDelayMs() > 0) {
log.info("Creating artificial delay of {} ms before allowing to report healthy status.", gracefulShutdownProperties.getStartupHealthyDelayMs());
}
}

@Override
Expand All @@ -33,6 +44,12 @@ public boolean canShutdown() {

@Override
public boolean isReady() {
return !startupInProgress;
var ready = startupTime != null && System.currentTimeMillis() - startupTime > gracefulShutdownProperties.getStartupHealthyDelayMs();

if (!readyDeclared.getAndSet(true)) {
log.info("Considering Service as fully started. Starting to broadcast UP state.");
}

return ready;
}
}
1 change: 1 addition & 0 deletions core/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.transferwise.common.gracefulshutdown.GracefulShutdownAutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=com.transferwise.common.gracefulshutdown.config.GracefulShutdownEnvironmentPostProcessor
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=2.14.5
version=2.15.0
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading