diff --git a/infrastructure/concurrency_controls.tf b/infrastructure/concurrency_controls.tf new file mode 100644 index 000000000..307f39dfa --- /dev/null +++ b/infrastructure/concurrency_controls.tf @@ -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 + }) +} diff --git a/infrastructure/lambda-concurrency-controller.tf b/infrastructure/lambda-concurrency-controller.tf new file mode 100644 index 000000000..d496c68bd --- /dev/null +++ b/infrastructure/lambda-concurrency-controller.tf @@ -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 +} diff --git a/infrastructure/schedules.tf b/infrastructure/schedules.tf index 5b86cbcf6..9fd00a50d 100644 --- a/infrastructure/schedules.tf +++ b/infrastructure/schedules.tf @@ -198,3 +198,5 @@ resource "aws_lambda_permission" "transfer_key_manager_schedule_permission" { aws_cloudwatch_event_rule.transfer_key_manager_schedule ] } + + diff --git a/infrastructure/variable.tf b/infrastructure/variable.tf index 272a4cbfd..20d397488 100644 --- a/infrastructure/variable.tf +++ b/infrastructure/variable.tf @@ -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 +} + +