From 563071b6cd692eb7fd9047ac125b4387584ba4bd Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 17 Nov 2025 15:27:50 +1300 Subject: [PATCH] [AWS AppConfig] Fix #228 - Expect ConnectException on shutdown - Don't log ConnectException on first occurrence as it's expected during shutdown so treat this as expected. - Reduce logging retries at startup to DEBUG to reduce log "noise" --- .../config/appconfig/AppConfigFetcher.java | 3 ++- .../config/appconfig/AppConfigPlugin.java | 18 ++++++++++++++---- .../config/appconfig/DAppConfigFetcher.java | 8 +++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigFetcher.java b/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigFetcher.java index 70fe7d73..76779975 100644 --- a/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigFetcher.java +++ b/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigFetcher.java @@ -1,5 +1,6 @@ package io.avaje.config.appconfig; +import java.net.ConnectException; import java.net.URI; interface AppConfigFetcher { @@ -8,7 +9,7 @@ static AppConfigFetcher.Builder builder() { return new DAppConfigFetcher.Builder(); } - Result fetch() throws FetchException; + Result fetch() throws ConnectException, FetchException; URI uri(); diff --git a/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigPlugin.java b/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigPlugin.java index a94e8d0d..3266ec79 100644 --- a/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigPlugin.java +++ b/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/AppConfigPlugin.java @@ -7,8 +7,11 @@ import static java.lang.System.Logger.Level.WARNING; import java.io.StringReader; +import java.lang.System.Logger.Level; +import java.net.ConnectException; import java.time.Instant; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; @@ -57,6 +60,7 @@ static final class Loader { private final AppConfigFetcher fetcher; private final ConfigParser yamlParser; private final ConfigParser propertiesParser; + private final AtomicInteger connectErrorCount = new AtomicInteger(); private final ReentrantLock lock = new ReentrantLock(); private final AtomicReference validUntil; private final long nextRefreshSeconds; @@ -98,14 +102,15 @@ static final class Loader { int initialLoad() { lock.lock(); try { - AppConfigFetcher.FetchException lastAttempt = null; + Exception lastAttempt = null; for (int i = 1; i < 11; i++) { try { loadAndPublish(); return i; - } catch (AppConfigFetcher.FetchException e) { + } catch (Exception e) { + // often seeing this with apps that start quickly (and AppConfig sidecar not up yet) lastAttempt = e; - log.log(INFO, "retrying, load attempt {0} got {1}", i, e.getMessage()); + log.log(DEBUG, "retrying, load attempt {0} got {1}", i, e.getMessage()); LockSupport.parkNanos(250_000_000); // 250 millis } } @@ -135,6 +140,11 @@ private void performReload() { } loadAndPublish(); + } catch (ConnectException e) { + // expected during shutdown when AppConfig sidecar shuts down before the app + int errCount = connectErrorCount.incrementAndGet(); + Level level = errCount > 1 ? WARNING : INFO; + log.log(level, "Failed to fetch from AwsAppConfig - likely shutdown in progress"); } catch (Exception e) { log.log(ERROR, "Error fetching or processing AwsAppConfig", e); } finally { @@ -145,7 +155,7 @@ private void performReload() { /** * Load and publish the configuration from AWS AppConfig. */ - private void loadAndPublish() throws AppConfigFetcher.FetchException { + private void loadAndPublish() throws AppConfigFetcher.FetchException, ConnectException { AppConfigFetcher.Result result = fetcher.fetch(); if (currentVersion.equals(result.version())) { log.log(TRACE, "AwsAppConfig unchanged, version {0}", currentVersion); diff --git a/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/DAppConfigFetcher.java b/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/DAppConfigFetcher.java index 51e1476f..2bad3230 100644 --- a/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/DAppConfigFetcher.java +++ b/avaje-aws-appconfig/src/main/java/io/avaje/config/appconfig/DAppConfigFetcher.java @@ -1,6 +1,6 @@ package io.avaje.config.appconfig; -import java.io.IOException; +import java.net.ConnectException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -23,7 +23,7 @@ public URI uri() { } @Override - public AppConfigFetcher.Result fetch() throws FetchException { + public AppConfigFetcher.Result fetch() throws ConnectException, FetchException { HttpRequest request = HttpRequest.newBuilder() .uri(uri) .GET() @@ -36,7 +36,9 @@ public AppConfigFetcher.Result fetch() throws FetchException { String body = res.body(); return new DResult(version, contentType, body); - } catch (IOException | InterruptedException e) { + } catch (ConnectException e) { + throw e; // expected on shutdown + } catch (Exception e) { throw new FetchException(e); } }