Skip to content

Commit 57ba501

Browse files
VIA-615 AJ/AS/SB implement rate limit for using content api
- add toNumber type converter to configs - add the additional environment variable to only the cache hydrator lambda, as it is the only component that calls the content api
1 parent 1afda5e commit 57ba501

File tree

7 files changed

+50
-34
lines changed

7 files changed

+50
-34
lines changed

infrastructure/environments/dev/main.tf

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
module "deploy_lambda" {
22
source = "../../modules/deploy_lambda"
33

4-
prefix = local.prefix
5-
nodejs_version = local.node_version
6-
cache_lambda_zip_path = local.cache_lambda_zip_path
7-
application_environment_variables = local.application_environment_variables
8-
log_retention_in_days = local.log_retention_in_days
9-
default_tags = local.default_tags
10-
alerting_sns_topic_arn = module.deploy_monitoring.alerting_sns_topic_arn
11-
account_id = data.aws_caller_identity.current.account_id
12-
region = local.region
4+
prefix = local.prefix
5+
nodejs_version = local.node_version
6+
cache_lambda_zip_path = local.cache_lambda_zip_path
7+
application_environment_variables = merge(local.application_environment_variables,
8+
{
9+
CONTENT_API_RATE_LIMIT_PER_MINUTE = 120,
10+
})
11+
log_retention_in_days = local.log_retention_in_days
12+
default_tags = local.default_tags
13+
alerting_sns_topic_arn = module.deploy_monitoring.alerting_sns_topic_arn
14+
account_id = data.aws_caller_identity.current.account_id
15+
region = local.region
1316
}
1417

1518
module "deploy" {

infrastructure/environments/preprod/main.tf

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
module "deploy_lambda" {
22
source = "../../modules/deploy_lambda"
33

4-
prefix = local.prefix
5-
nodejs_version = local.node_version
6-
cache_lambda_zip_path = local.cache_lambda_zip_path
7-
application_environment_variables = local.application_environment_variables
8-
log_retention_in_days = local.log_retention_in_days
9-
default_tags = local.default_tags
10-
alerting_sns_topic_arn = module.deploy_monitoring.alerting_sns_topic_arn
11-
account_id = data.aws_caller_identity.current.account_id
12-
region = local.region
4+
prefix = local.prefix
5+
nodejs_version = local.node_version
6+
cache_lambda_zip_path = local.cache_lambda_zip_path
7+
application_environment_variables = merge(local.application_environment_variables,
8+
{
9+
CONTENT_API_RATE_LIMIT_PER_MINUTE = 1200,
10+
})
11+
log_retention_in_days = local.log_retention_in_days
12+
default_tags = local.default_tags
13+
alerting_sns_topic_arn = module.deploy_monitoring.alerting_sns_topic_arn
14+
account_id = data.aws_caller_identity.current.account_id
15+
region = local.region
1316
}
1417

1518
module "deploy" {

infrastructure/environments/prod/main.tf

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
module "deploy_lambda" {
22
source = "../../modules/deploy_lambda"
33

4-
prefix = local.prefix
5-
nodejs_version = local.node_version
6-
cache_lambda_zip_path = local.cache_lambda_zip_path
7-
application_environment_variables = local.application_environment_variables
8-
log_retention_in_days = local.log_retention_in_days
9-
default_tags = local.default_tags
10-
alerting_sns_topic_arn = module.deploy_monitoring.alerting_sns_topic_arn
11-
account_id = data.aws_caller_identity.current.account_id
12-
region = local.region
4+
prefix = local.prefix
5+
nodejs_version = local.node_version
6+
cache_lambda_zip_path = local.cache_lambda_zip_path
7+
application_environment_variables = merge(local.application_environment_variables,
8+
{
9+
CONTENT_API_RATE_LIMIT_PER_MINUTE = 1200,
10+
})
11+
log_retention_in_days = local.log_retention_in_days
12+
default_tags = local.default_tags
13+
alerting_sns_topic_arn = module.deploy_monitoring.alerting_sns_topic_arn
14+
account_id = data.aws_caller_identity.current.account_id
15+
region = local.region
1316
}
1417

1518
module "deploy" {

infrastructure/environments/test/main.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ module "deploy_lambda" {
66
cache_lambda_zip_path = local.cache_lambda_zip_path
77
application_environment_variables = merge(local.application_environment_variables,
88
{
9-
ELIGIBILITY_API_ENDPOINT = "${module.deploy_fake_api.application_url}/",
10-
APIM_AUTH_URL = "${module.deploy_fake_api.application_url}/oauth2/token"
9+
ELIGIBILITY_API_ENDPOINT = "${module.deploy_fake_api.application_url}/",
10+
APIM_AUTH_URL = "${module.deploy_fake_api.application_url}/oauth2/token",
11+
CONTENT_API_RATE_LIMIT_PER_MINUTE = 120,
1112
})
1213
log_retention_in_days = local.log_retention_in_days
1314
default_tags = local.default_tags

src/_lambda/content-cache-hydrator/handler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ const runContentCacheHydrator = async (event: ContentCacheHydratorEvent) => {
147147
let failureCount: number = 0;
148148
let invalidatedCount: number = 0;
149149

150+
const rateLimitDelayMillis: number = 1000 / ((await config.CONTENT_API_RATE_LIMIT_PER_MINUTE) / 60);
150151
for (const vaccine of vaccinesToRunOn) {
151152
const status = await hydrateCacheForVaccine(
152153
vaccine,
@@ -155,6 +156,7 @@ const runContentCacheHydrator = async (event: ContentCacheHydratorEvent) => {
155156
);
156157
invalidatedCount += status.invalidatedCount;
157158
failureCount += status.failureCount;
159+
await new Promise((f) => setTimeout(f, rateLimitDelayMillis)); // sleep
158160
}
159161

160162
log.info({ context: { failureCount, invalidatedCount } }, "Finished hydrating content cache: report");

src/utils/config.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface AppConfig {
1919

2020
// Environment Variables in Lambda
2121
CONTENT_API_ENDPOINT: URL;
22+
CONTENT_API_RATE_LIMIT_PER_MINUTE: number;
2223
ELIGIBILITY_API_ENDPOINT: URL;
2324
CONTENT_CACHE_PATH: string;
2425
CONTENT_CACHE_IS_CHANGE_APPROVAL_ENABLED: boolean;
@@ -73,6 +74,11 @@ class Config {
7374
private ttlExpiresAt: number = Date.now() + Config.CACHE_TTL_MILLIS;
7475
static readonly CACHE_TTL_MILLIS: number = 300 * 1000;
7576

77+
private static readonly toNumber = (value: string): number | undefined => {
78+
const num = Number(value);
79+
if (!Number.isNaN(num)) return num;
80+
return undefined;
81+
};
7682
private static readonly toUrl = (value: string): URL => new URL(value);
7783
private static readonly toBoolean = (value: string): boolean | undefined => {
7884
const lower = value.toLowerCase();
@@ -83,17 +89,14 @@ class Config {
8389
static readonly converters: Record<string, (value: string) => ConfigValue> = {
8490
APIM_AUTH_URL: Config.toUrl,
8591
CONTENT_API_ENDPOINT: Config.toUrl,
92+
CONTENT_API_RATE_LIMIT_PER_MINUTE: Config.toNumber,
8693
ELIGIBILITY_API_ENDPOINT: Config.toUrl,
8794
NBS_URL: Config.toUrl,
8895
NHS_LOGIN_URL: Config.toUrl,
8996
CONTENT_CACHE_IS_CHANGE_APPROVAL_ENABLED: Config.toBoolean,
9097
NHS_APP_REDIRECT_LOGIN_URL: Config.toUrl,
9198
IS_APIM_AUTH_ENABLED: Config.toBoolean,
92-
MAX_SESSION_AGE_MINUTES: (value: string) => {
93-
const num = Number(value);
94-
if (!Number.isNaN(num)) return num;
95-
return undefined;
96-
},
99+
MAX_SESSION_AGE_MINUTES: Config.toNumber,
97100
};
98101

99102
/**

test-data/config/builders.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class ConfigBuilder {
99
constructor() {
1010
this._configValues = {
1111
CONTENT_API_ENDPOINT: randomURL(),
12+
CONTENT_API_RATE_LIMIT_PER_MINUTE: randomInteger(10000, 100000),
1213
ELIGIBILITY_API_ENDPOINT: randomURL(),
1314
CONTENT_API_KEY: randomString(10),
1415
ELIGIBILITY_API_KEY: randomString(10),

0 commit comments

Comments
 (0)