Skip to content

Commit b2cb423

Browse files
authored
Merge pull request #1915 from kekey1/OCD-5174
fix: Add logging, exception/null handling to SBUL creator job
2 parents 485f0dd + e2bdea5 commit b2cb423

5 files changed

Lines changed: 41 additions & 31 deletions

File tree

chpl/chpl-api/pom.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,6 @@
126126
</exclusions>
127127
</dependency>
128128

129-
<!-- Rate limiting -->
130-
<dependency>
131-
<groupId>com.github.vladimir-bukhtoyarov</groupId>
132-
<artifactId>bucket4j-core</artifactId>
133-
<version>7.6.0</version>
134-
</dependency>
135-
136129
<!-- provided dependencies -->
137130
<dependency>
138131
<groupId>org.projectlombok</groupId>

chpl/chpl-api/src/main/java/gov/healthit/chpl/ratelimiting/RateLimitingInterceptor.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,15 @@
66
import java.util.List;
77
import java.util.Map;
88

9-
import jakarta.servlet.http.HttpServletRequest;
10-
import jakarta.servlet.http.HttpServletResponse;
11-
129
import org.apache.commons.lang3.StringUtils;
1310
import org.springframework.http.HttpStatus;
1411
import org.springframework.web.servlet.HandlerInterceptor;
1512

1613
import gov.healthit.chpl.api.dao.ApiKeyDAO;
1714
import gov.healthit.chpl.util.ErrorMessageUtil;
18-
import io.github.bucket4j.Bandwidth;
1915
import io.github.bucket4j.Bucket;
20-
import io.github.bucket4j.Refill;
16+
import jakarta.servlet.http.HttpServletRequest;
17+
import jakarta.servlet.http.HttpServletResponse;
2118
import lombok.extern.log4j.Log4j2;
2219

2320
@Log4j2
@@ -66,10 +63,12 @@ public boolean preHandle(final HttpServletRequest request,
6663

6764
private Bucket getBucketForApiKey(String apiKey) {
6865
if (!rateLimitingBuckets.containsKey(apiKey)) {
66+
6967
rateLimitingBuckets.put(apiKey, Bucket.builder()
70-
.addLimit(Bandwidth.classic(rateLimitRequestCount,
71-
Refill.intervally(rateLimitRequestCount, Duration.ofSeconds(rateLimitTimePeriod))))
72-
.build());
68+
.addLimit(limit -> limit
69+
.capacity(rateLimitRequestCount)
70+
.refillIntervally(rateLimitRequestCount, Duration.ofSeconds(rateLimitTimePeriod)))
71+
.build());
7372
}
7473
return rateLimitingBuckets.get(apiKey);
7574
}

chpl/chpl-service/pom.xml

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,6 @@
181181
</exclusion>
182182
</exclusions>
183183
</dependency>
184-
<!--dependency>
185-
<groupId>commons-fileupload</groupId>
186-
<artifactId>commons-fileupload</artifactId>
187-
<version>${commons.fileupload.version}</version>
188-
<exclusions>
189-
<exclusion>
190-
<groupId>commons-io</groupId>
191-
<artifactId>commons-io</artifactId>
192-
</exclusion>
193-
</exclusions>
194-
</dependency-->
195184
<dependency>
196185
<groupId>commons-io</groupId>
197186
<artifactId>commons-io</artifactId>
@@ -361,6 +350,14 @@
361350
<artifactId>datadog-api-client</artifactId>
362351
<version>2.48.0</version>
363352
</dependency>
353+
<!-- Provides throttling and rate-limiting behavior-->
354+
<!-- Specifically being used to slow down our requests to the Datadog API to avoid being rate-limited -->
355+
<dependency>
356+
<groupId>com.bucket4j</groupId>
357+
<artifactId>bucket4j_jdk17-core</artifactId>
358+
<version>8.16.1</version>
359+
<scope>compile</scope>
360+
</dependency>
364361

365362
<!-- jars provided by the system and not included in the packaged jar -->
366363
<dependency>

chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/urluptime/DatadogSyntheticsTestResultService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public SyntheticsAPITestResultFull getDetailedTestResult(String publicTestKey, S
7070
try {
7171
return apiProvider.getApiInstance().getAPITestResult(publicTestKey, resultId);
7272
} catch (ApiException e) {
73-
LOGGER.error("Could not retrieve test result details for: {} | {}", publicTestKey, resultId);
73+
LOGGER.error("Could not retrieve test result details for: {} | {}", publicTestKey, resultId, e);
74+
LOGGER.error("Datadog API error response: " + e.getResponseBody());
7475
return null;
7576
}
7677
}

chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/urluptime/DatadogUrlUptimeSynchonizer.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gov.healthit.chpl.scheduler.job.urluptime;
22

33
import java.time.DayOfWeek;
4+
import java.time.Duration;
45
import java.time.Instant;
56
import java.time.LocalDate;
67
import java.time.LocalDateTime;
@@ -30,24 +31,28 @@
3031
import gov.healthit.chpl.domain.CertificationBody;
3132
import gov.healthit.chpl.domain.Developer;
3233
import gov.healthit.chpl.exception.ValidationException;
34+
import io.github.bucket4j.Bucket;
3335
import lombok.extern.log4j.Log4j2;
3436

3537
@Log4j2(topic = "serviceBaseUrlListUptimeCreatorJobLogger")
3638
@Component
3739
public class DatadogUrlUptimeSynchonizer {
3840
private static final Long DAYS_TO_LOOK_BACK_FOR_RESULTS = 7L;
41+
private static final long DATADOG_REQUESTS_PER_MINUTE = 90; //the actual limit is 100 but I don't feel like we need to cut it that close
42+
private static final long DATADOG_REQUESTS_BURST_LIMIT = 5;
3943

4044
// VARIABLE NAMING CONVENTION
4145
// ServiceBaseUrlList - these are service base url lists collected from the listings in CHPL, grouped by developer
4246
// DatadogSyntheticsTest - these are synthetic tests that exist in Datadog
43-
// UrlUptimeMonitor - these are reporting entities that are store in the table url_uptime_monitors
47+
// UrlUptimeMonitor - these are reporting entities that are store in the table url_uptime_monitor
4448

4549
private DatadogSyntheticsTestService datadogSyntheticsTestService;
4650
private DatadogSyntheticsTestResultService datadogSyntheticsTestResultService;
4751
private ServiceBaseUrlListService serviceBaseUrlListService;
4852
private UrlUptimeMonitorDAO urlUptimeMonitorDAO;
4953
private UrlUptimeMonitorTestDAO urlUptimeMonitorTestDAO;
5054
private DeveloperSearchService developerSearchService;
55+
private Bucket bucket;
5156

5257
private List<String> errorsToIgnore;
5358

@@ -61,7 +66,16 @@ public DatadogUrlUptimeSynchonizer(DatadogSyntheticsTestService datadogSynthetic
6166
this.urlUptimeMonitorDAO = urlUptimeMonitorDAO;
6267
this.urlUptimeMonitorTestDAO = urlUptimeMonitorTestDAO;
6368
this.developerSearchService = developerSearchService;
64-
69+
this.bucket = Bucket.builder()
70+
// 1. Sustained Limit: 90 requests per 1 minute
71+
.addLimit(limit -> limit
72+
.capacity(DATADOG_REQUESTS_PER_MINUTE)
73+
.refillGreedy(DATADOG_REQUESTS_PER_MINUTE, Duration.ofMinutes(1)))
74+
// 2. Burst Limit: Maximum 5 requests per 1 second
75+
.addLimit(limit -> limit
76+
.capacity(DATADOG_REQUESTS_BURST_LIMIT)
77+
.refillGreedy(DATADOG_REQUESTS_BURST_LIMIT, Duration.ofSeconds(1)))
78+
.build();
6579
errorsToIgnore = List.of("BODY_TOO_LARGE_TO_PROCESS");
6680
}
6781

@@ -82,12 +96,18 @@ private void synchronizeUrlUptimeMonitorTestsWithDatadogSyntheticsTestResults()
8296
.forEach(testDate -> urlUptimeMonitorDAO.getAll().forEach(urlUptimeMonitor -> {
8397
String publicId = getDatadogPublicId(syntheticsTestDetails, urlUptimeMonitor.getUrl(), urlUptimeMonitor.getDeveloper().getId());
8498
datadogSyntheticsTestResultService.getSyntheticsTestResults(publicId, testDate).forEach(syntheticsTestResult -> {
99+
try {
100+
//Blocks until tokens for BOTH limits are available
101+
bucket.asBlocking().consumeUninterruptibly(1);
85102
urlUptimeMonitorTestDAO.create(UrlUptimeMonitorTest.builder()
86103
.urlUptimeMonitorId(urlUptimeMonitor.getId())
87104
.datadogTestKey(syntheticsTestResult.getResultId())
88105
.checkTime(toLocalDateTime(syntheticsTestResult.getCheckTime().longValue()))
89106
.passed(calculatePassed(syntheticsTestResult, publicId))
90107
.build());
108+
} catch (Exception ex) {
109+
LOGGER.error("Could not process url_uptime_monitor " + publicId + " for url " + urlUptimeMonitor.getUrl() + " and developer " + urlUptimeMonitor.getDeveloper().getId());
110+
}
91111
});
92112
}));
93113
}
@@ -97,7 +117,7 @@ private boolean calculatePassed(SyntheticsAPITestResultShort result, String publ
97117
return true;
98118
} else {
99119
SyntheticsAPITestResultFull detailedResult = datadogSyntheticsTestResultService.getDetailedTestResult(publicId, result.getResultId());
100-
return isErrorIgnorable(detailedResult.getResult().getFailure().getCode());
120+
return detailedResult != null && isErrorIgnorable(detailedResult.getResult().getFailure().getCode());
101121
}
102122
}
103123

@@ -199,7 +219,7 @@ private void removeOutdatedUrlMonitors(List<UrlUptimeMonitor> existing, List<Url
199219
existing.stream()
200220
.filter(uum -> !contains(expected, uum))
201221
.forEach(urlMonitor -> {
202-
LOGGER.info("Removing the following URL to url_uptime_monitor table: {}, {}", urlMonitor.getUrl(), urlMonitor.getDeveloper().getId());
222+
LOGGER.info("Removing the following URL to url_uptime_monitor table: {}, {}, {}", urlMonitor.getUrl(), urlMonitor.getDeveloper().getId(), urlMonitor.getDatadogPublicId());
203223
urlUptimeMonitorDAO.delete(urlMonitor);
204224
});
205225
}

0 commit comments

Comments
 (0)