Skip to content
39 changes: 39 additions & 0 deletions infrastructure/concurrency_controls.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Concurrency control schedules
# These times are set to ensure 9 AM - 7 PM UK local time is always covered regardless of GMT/BST:
# - During GMT (winter): 8 AM UTC = 8 AM local, 7 PM UTC = 7 PM local (covers 9 AM - 7 PM with buffer)
# - During BST (summer): 8 AM UTC = 9 AM local, 7 PM UTC = 8 PM local (covers 9 AM - 7 PM with buffer)
# This guarantees the core working hours (9 AM - 7 PM UK time) always have reduced concurrency.

# Office hours start (8 AM UTC)
resource "aws_cloudwatch_event_rule" "bulk_upload_concurrency_office_hours_start" {
name = "bulk-upload-office-hours-start"
schedule_expression = "cron(0 8 * * ? *)"
}

resource "aws_cloudwatch_event_target" "bulk_upload_concurrency_office_hours_start" {
rule = aws_cloudwatch_event_rule.bulk_upload_concurrency_office_hours_start.name
target_id = "office-hours-start"
arn = module.concurrency-controller-lambda.lambda_arn

input = jsonencode({
targetFunction = module.bulk-upload-lambda.function_name
reservedConcurrency = var.office_hours_start_concurrency
})
}

# Office hours stop (7 PM UTC / 19:00 UTC)
resource "aws_cloudwatch_event_rule" "bulk_upload_concurrency_office_hours_stop" {
name = "bulk-upload-office-hours-stop"
schedule_expression = "cron(0 19 * * ? *)"
}

resource "aws_cloudwatch_event_target" "bulk_upload_concurrency_office_hours_stop" {
rule = aws_cloudwatch_event_rule.bulk_upload_concurrency_office_hours_stop.name
target_id = "office-hours-stop"
arn = module.concurrency-controller-lambda.lambda_arn

input = jsonencode({
targetFunction = module.bulk-upload-lambda.function_name
reservedConcurrency = var.office_hours_end_concurrency
})
}
45 changes: 45 additions & 0 deletions infrastructure/lambda-concurrency-controller.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

data "aws_iam_policy_document" "concurrency_controller_policy" {
statement {
effect = "Allow"
actions = [
"lambda:PutFunctionConcurrency",
"lambda:GetFunctionConcurrency"
]
resources = [
module.bulk-upload-lambda.lambda_arn
]
}
}

module "concurrency-controller-lambda" {
source = "./modules/lambda"
name = "ConcurrencyController"
handler = "handlers.concurrency_controller_handler.lambda_handler"

#This lambda is an orchestrator so should have unlimited conc
reserved_concurrent_executions = -1

is_gateway_integration_needed = false
is_invoked_from_gateway = false

iam_role_policy_documents = [
data.aws_iam_policy_document.concurrency_controller_policy.json
]
}

resource "aws_lambda_permission" "office_hours_start_permission" {
statement_id = "AllowEventBridgeOfficeHoursStart"
action = "lambda:InvokeFunction"
function_name = module.concurrency-controller-lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.bulk_upload_concurrency_office_hours_start.arn
}

resource "aws_lambda_permission" "office_hours_stop_permission" {
statement_id = "AllowEventBridgeOfficeHoursStop"
action = "lambda:InvokeFunction"
function_name = module.concurrency-controller-lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.bulk_upload_concurrency_office_hours_stop.arn
}
2 changes: 2 additions & 0 deletions infrastructure/schedules.tf
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,5 @@ resource "aws_lambda_permission" "transfer_key_manager_schedule_permission" {
aws_cloudwatch_event_rule.transfer_key_manager_schedule
]
}


14 changes: 14 additions & 0 deletions infrastructure/variable.tf
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,17 @@ variable "ssh_key_management_dry_run" {
type = bool
default = false
}

# Concurrency Controller

variable "office_hours_start_concurrency" {
type = number
default = 1
}

variable "office_hours_end_concurrency" {
type = number
default = 3
}