diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 66a5b255..4819a65a 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -546,6 +546,8 @@ jobs: API_WEBHOOK_ENDPOINT: https://api-staging.knowhereto.ai/v1/internal/s3-events SNS_TOPIC_ARN: arn:aws:sns:us-east-1:107424103509:knowhere-staging-s3-events QSTASH_CALLBACK_BASE_URL: https://api-staging.knowhereto.ai/api/v1 + API_CPU: "256" + API_MEMORY: "1024" WORKER_CPU: "2048" WORKER_MEMORY: "4096" shell: bash @@ -772,6 +774,8 @@ jobs: API_WEBHOOK_ENDPOINT: https://api.knowhereto.ai/v1/internal/s3-events SNS_TOPIC_ARN: arn:aws:sns:us-east-1:107424103509:knowhere-prod-s3-events QSTASH_CALLBACK_BASE_URL: https://api.knowhereto.ai/api/v1 + API_CPU: "512" + API_MEMORY: "2048" WORKER_CPU: "2048" WORKER_MEMORY: "4096" shell: bash diff --git a/deploy/ecs/README.md b/deploy/ecs/README.md index dd0742b4..7b7d6170 100644 --- a/deploy/ecs/README.md +++ b/deploy/ecs/README.md @@ -32,6 +32,8 @@ FRONTEND_URL=https://staging.knowhereto.ai \ API_WEBHOOK_ENDPOINT=https://api-staging.knowhereto.ai/v1/internal/s3-events \ SNS_TOPIC_ARN=arn:aws:sns:us-east-1:107424103509:knowhere-staging-s3-events \ QSTASH_CALLBACK_BASE_URL=https://api-staging.knowhereto.ai/api/v1 \ +API_CPU=256 \ +API_MEMORY=1024 \ WORKER_CPU=2048 \ WORKER_MEMORY=4096 \ python deploy/ecs/render_task_definitions.py --environment staging --output-dir /tmp/knowhere-ecs-rendered @@ -39,6 +41,10 @@ python deploy/ecs/render_task_definitions.py --environment staging --output-dir The output directory is deployment-only and must not be committed. The renderer fails on missing inputs, unresolved placeholders, or either long-lived S3 credential variable. +The staging workflow keeps the API at 256 CPU / 1024 MiB. The production release +workflow sets the API to 512 CPU / 2048 MiB (2 GiB) and the worker to 2048 CPU / +4096 MiB, so production API memory remains at 2 GiB across future releases. + ## Staging workflow prerequisites The staging workflow in `.github/workflows/build-images.yml` expects these GitHub Actions secrets: diff --git a/deploy/ecs/render_task_definitions.py b/deploy/ecs/render_task_definitions.py index 458cb8a7..33e2b9e5 100644 --- a/deploy/ecs/render_task_definitions.py +++ b/deploy/ecs/render_task_definitions.py @@ -32,6 +32,8 @@ "API_WEBHOOK_ENDPOINT", "SNS_TOPIC_ARN", "QSTASH_CALLBACK_BASE_URL", + "API_CPU", + "API_MEMORY", "WORKER_CPU", "WORKER_MEMORY", ) diff --git a/deploy/ecs/task-definition-api.staging.json b/deploy/ecs/task-definition-api.staging.json index 0e1013c9..0b4af2c5 100644 --- a/deploy/ecs/task-definition-api.staging.json +++ b/deploy/ecs/task-definition-api.staging.json @@ -4,8 +4,8 @@ "executionRoleArn": "${EXECUTION_ROLE_ARN}", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], - "cpu": "256", - "memory": "1024", + "cpu": "${API_CPU}", + "memory": "${API_MEMORY}", "runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" diff --git a/deploy/ecs/test_render_task_definitions.py b/deploy/ecs/test_render_task_definitions.py index b9c1f569..ff7efbee 100644 --- a/deploy/ecs/test_render_task_definitions.py +++ b/deploy/ecs/test_render_task_definitions.py @@ -33,6 +33,8 @@ "API_WEBHOOK_ENDPOINT": "https://api-staging.knowhereto.ai/v1/internal/s3-events", "SNS_TOPIC_ARN": "arn:aws:sns:us-east-1:107424103509:knowhere-staging-s3-events", "QSTASH_CALLBACK_BASE_URL": "https://api-staging.knowhereto.ai/api/v1", + "API_CPU": "256", + "API_MEMORY": "1024", "WORKER_CPU": "2048", "WORKER_MEMORY": "4096", } @@ -155,6 +157,20 @@ def test_staging_worker_preserves_evidence_selected_capacity(tmp_path: Path) -> assert definition["memory"] == "4096" +def test_staging_api_preserves_selected_capacity(tmp_path: Path) -> None: + """API capacity remains at the verified staging size.""" + output_path: Path = tmp_path / "task-definition-api.staging.json" + render_template( + TEMPLATE_DIRECTORY / "task-definition-api.staging.json", + output_path, + RENDER_VARIABLES, + ) + definition: dict[str, object] = json.loads(output_path.read_text(encoding="utf-8")) + + assert definition["cpu"] == "256" + assert definition["memory"] == "1024" + + def test_renderer_rejects_forbidden_s3_credential_variable() -> None: """Task definitions must never inject long-lived S3 credentials.""" definition: dict[str, object] = {