1010import java .time .Instant ;
1111import java .util .Map ;
1212import java .util .concurrent .atomic .AtomicReference ;
13+ import java .util .concurrent .locks .LockSupport ;
1314import java .util .concurrent .locks .ReentrantLock ;
1415
1516import io .avaje .applog .AppLog ;
@@ -37,7 +38,10 @@ public void load(Configuration configuration) {
3738 return ;
3839 }
3940 loader = new Loader (configuration );
40- loader .reload ();
41+ int attempts = loader .initialLoad ();
42+ if (attempts > 1 ){
43+ log .log (INFO , "AwsAppConfig loaded after {} attempts" , attempts + 1 );
44+ }
4145 }
4246
4347 @ Override
@@ -88,6 +92,31 @@ static final class Loader {
8892 }
8993 }
9094
95+ /**
96+ * Potential race conditional with AWS AppConfig sidecar so use simple retry loop.
97+ */
98+ int initialLoad () {
99+ lock .lock ();
100+ try {
101+ AppConfigFetcher .FetchException lastAttempt = null ;
102+ for (int i = 1 ; i < 11 ; i ++) {
103+ try {
104+ loadAndPublish ();
105+ return i ;
106+ } catch (AppConfigFetcher .FetchException e ) {
107+ lastAttempt = e ;
108+ log .log (INFO , "retrying, load attempt {} got {}" , i , e .getMessage ());
109+ LockSupport .parkNanos (250_000_000 ); // 250 millis
110+ }
111+ }
112+ log .log (ERROR , "Failed initial AwsAppConfig load" , lastAttempt );
113+ return -1 ;
114+
115+ } finally {
116+ lock .unlock ();
117+ }
118+ }
119+
91120 void reload () {
92121 if (reloadRequired ()) {
93122 performReload ();
@@ -104,24 +133,7 @@ private void performReload() {
104133 if (!reloadRequired ()) {
105134 return ;
106135 }
107- AppConfigFetcher .Result result = fetcher .fetch ();
108- if (currentVersion .equals (result .version ())) {
109- log .log (TRACE , "AwsAppConfig unchanged, version {0}" , currentVersion );
110- } else {
111- String contentType = result .contentType ();
112- if (log .isLoggable (TRACE )) {
113- int contentLength = result .body ().length ();
114- log .log (TRACE , "AwsAppConfig fetched version:{0} contentType:{1} contentLength:{2,number,#}" , result .version (), contentType , contentLength );
115- }
116- Map <String , String > keyValues = parse (result );
117- configuration .eventBuilder ("AwsAppConfig" )
118- .putAll (keyValues )
119- .publish ();
120- currentVersion = result .version ();
121- debugLog (result , keyValues .size ());
122- }
123- // move the next valid until time
124- validUntil .set (Instant .now ().plusSeconds (nextRefreshSeconds ));
136+ loadAndPublish ();
125137
126138 } catch (Exception e ) {
127139 log .log (ERROR , "Error fetching or processing AwsAppConfig" , e );
@@ -130,6 +142,30 @@ private void performReload() {
130142 }
131143 }
132144
145+ /**
146+ * Load and publish the configuration from AWS AppConfig.
147+ */
148+ private void loadAndPublish () throws AppConfigFetcher .FetchException {
149+ AppConfigFetcher .Result result = fetcher .fetch ();
150+ if (currentVersion .equals (result .version ())) {
151+ log .log (TRACE , "AwsAppConfig unchanged, version {0}" , currentVersion );
152+ } else {
153+ String contentType = result .contentType ();
154+ if (log .isLoggable (TRACE )) {
155+ int contentLength = result .body ().length ();
156+ log .log (TRACE , "AwsAppConfig fetched version:{0} contentType:{1} contentLength:{2,number,#}" , result .version (), contentType , contentLength );
157+ }
158+ Map <String , String > keyValues = parse (result );
159+ configuration .eventBuilder ("AwsAppConfig" )
160+ .putAll (keyValues )
161+ .publish ();
162+ currentVersion = result .version ();
163+ debugLog (result , keyValues .size ());
164+ }
165+ // move the next valid until time
166+ validUntil .set (Instant .now ().plusSeconds (nextRefreshSeconds ));
167+ }
168+
133169 private Map <String , String > parse (AppConfigFetcher .Result result ) {
134170 ConfigParser parser = parser (result .contentType ());
135171 return parser .load (new StringReader (result .body ()));
0 commit comments