2424import java .io .IOException ;
2525import java .net .MalformedURLException ;
2626import java .net .URL ;
27+ import java .util .HashMap ;
28+ import java .util .Map ;
2729import java .util .concurrent .ConcurrentHashMap ;
2830import java .util .concurrent .ConcurrentMap ;
2931import java .util .concurrent .ExecutionException ;
3032import java .util .concurrent .Future ;
31- import java .util .concurrent .TimeUnit ;
3233
3334import com .microsoft .applicationinsights .internal .logger .InternalLogger ;
3435import com .microsoft .applicationinsights .internal .shutdown .SDKShutdownActivity ;
35- import com .microsoft .applicationinsights .internal .shutdown .Stoppable ;
3636
37- import org .apache .commons .lang3 .exception .ExceptionUtils ;
3837import org .apache .http .HttpResponse ;
3938import org .apache .http .ParseException ;
4039import org .apache .http .client .config .RequestConfig ;
4140import org .apache .http .client .methods .HttpGet ;
4241import org .apache .http .impl .nio .client .CloseableHttpAsyncClient ;
4342import org .apache .http .impl .nio .client .HttpAsyncClients ;
44- import org .apache .http .nio .client .HttpAsyncClient ;
4543import org .apache .http .util .EntityUtils ;
4644
4745public class CdsProfileFetcher implements AppProfileFetcher {
@@ -50,9 +48,13 @@ public class CdsProfileFetcher implements AppProfileFetcher {
5048 private String endpointAddress ;
5149 private static final String ProfileQueryEndpointAppIdFormat = "%s/api/profiles/%s/appId" ;
5250 private static final String DefaultProfileQueryEndpointAddress = "https://dc.services.visualstudio.com" ;
51+ private static final int MAX_RETRIES = 3 ;
5352
5453 // cache of tasks per ikey
5554 private final ConcurrentMap <String , Future <HttpResponse >> tasks ;
55+
56+ // failure counters per ikey
57+ private final Map <String , Integer > failureCounters ;
5658
5759 public CdsProfileFetcher () {
5860 RequestConfig requestConfig = RequestConfig .custom ()
@@ -63,11 +65,14 @@ public CdsProfileFetcher() {
6365
6466 setHttpClient (HttpAsyncClients .custom ()
6567 .setDefaultRequestConfig (requestConfig )
68+ .useSystemProperties ()
6669 .build ());
6770
6871 this .httpClient .start ();
6972
7073 this .tasks = new ConcurrentHashMap <String , Future <HttpResponse >>();
74+ this .failureCounters = new HashMap <String , Integer >();
75+
7176 this .endpointAddress = DefaultProfileQueryEndpointAddress ;
7277 }
7378
@@ -79,6 +84,14 @@ public ProfileFetcherResult fetchAppProfile(String instrumentationKey) throws In
7984 }
8085
8186 ProfileFetcherResult result = new ProfileFetcherResult (null , ProfileFetcherResultTaskStatus .PENDING );
87+
88+ // check if we have tried resolving this ikey too many times. If so, quit to save on perf.
89+ Integer failureCounter = this .failureCounters .get (instrumentationKey );
90+ if (failureCounter != null && failureCounter .intValue () >= MAX_RETRIES ) {
91+ InternalLogger .INSTANCE .warn ("The profile fetch task will not execute. Max number of retries reached." );
92+ return result ;
93+ }
94+
8295 Future <HttpResponse > currentTask = this .tasks .get (instrumentationKey );
8396
8497 // if no task currently exists for this ikey, then let's create one.
@@ -97,18 +110,24 @@ public ProfileFetcherResult fetchAppProfile(String instrumentationKey) throws In
97110 HttpResponse response = currentTask .get ();
98111
99112 if (response .getStatusLine ().getStatusCode () != 200 ) {
113+ incrementFailureCount (instrumentationKey );
100114 return new ProfileFetcherResult (null , ProfileFetcherResultTaskStatus .FAILED );
101115 }
102116
103117 String appId = EntityUtils .toString (response .getEntity ());
104118
105119 //check for case when breeze returns invalid value
106120 if (appId == null || appId .isEmpty ()) {
121+ incrementFailureCount (instrumentationKey );
107122 return new ProfileFetcherResult (null , ProfileFetcherResultTaskStatus .FAILED );
108123 }
109124
110125 return new ProfileFetcherResult (appId , ProfileFetcherResultTaskStatus .COMPLETE );
111126
127+ } catch (Exception ex ) {
128+ incrementFailureCount (instrumentationKey );
129+ throw ex ;
130+
112131 } finally {
113132 // remove task as we're done with it.
114133 this .tasks .remove (instrumentationKey );
@@ -133,6 +152,15 @@ private Future<HttpResponse> createFetchTask(String instrumentationKey) {
133152 return this .httpClient .execute (request , null );
134153 }
135154
155+ private synchronized void incrementFailureCount (String instrumentationKey ) {
156+ Integer failureCounter = this .failureCounters .get (instrumentationKey );
157+ if (failureCounter == null ) {
158+ this .failureCounters .put (instrumentationKey , new Integer (1 ));
159+ } else {
160+ this .failureCounters .put (instrumentationKey , new Integer (failureCounter .intValue () + 1 ));
161+ }
162+ }
163+
136164 @ Override
137165 public void close () throws IOException {
138166 this .httpClient .close ();
0 commit comments