Skip to content

Commit 0f5a5ec

Browse files
Perf/stress test findings revisions (#623)
1 parent 6de5862 commit 0f5a5ec

7 files changed

Lines changed: 47 additions & 1 deletion

File tree

backend/app/core/db.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
engine = create_engine(
77
str(settings.SQLALCHEMY_DATABASE_URI),
88
echo=settings.ECHO_DB,
9+
# 60/task; RDS max_connections ~900 caps us at ~14 tasks. Keep in step
10+
# with the anyio threadpool limiter (app/main.py lifespan).
11+
pool_size=40,
12+
max_overflow=20,
913
pool_pre_ping=True,
1014
pool_recycle=3600,
1115
)

backend/app/evaluation/graph.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ def get_gerrydb_graph(file_path: str) -> Graph:
5353
return pickle.load(f)
5454

5555

56-
_GRAPH_CACHE_MAX_SIZE = 10
56+
# Must exceed the distinct-map working set or evictions force multi-second
57+
# cold S3 reloads; each cached graph costs real memory, so raise with care.
58+
_GRAPH_CACHE_MAX_SIZE = 15
5759

5860

5961
@lru_cache(maxsize=_GRAPH_CACHE_MAX_SIZE)

backend/app/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
)
99
from fastapi.responses import JSONResponse, Response
1010
from typing import Annotated, Any
11+
import anyio
1112
import msgpack
1213
import psutil
1314
import time
@@ -116,6 +117,9 @@
116117

117118
@asynccontextmanager
118119
async def lifespan(app: FastAPI):
120+
# Sync-route concurrency; default 40 would cap below the DB pool
121+
# (60/task, app/core/db.py). Needs a running event loop, hence lifespan.
122+
anyio.to_thread.current_default_thread_limiter().total_tokens = 80
119123
yield
120124

121125

backend/stress_test/runner/provision.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ REPO_SHA="${REPO_SHA:?Set REPO_SHA to the pinned commit containing backend/stres
2020
# Backend S3 bucket (pulumi config s3BucketName / task env R2_BUCKET_NAME);
2121
# artifacts live under stress-test/.
2222
RESULTS_BUCKET="${RESULTS_BUCKET:?Set RESULTS_BUCKET to the backend S3 bucket name}"
23+
# Easy slip: the ALB access-logs bucket, which the IAM policy can't write to.
24+
case "$RESULTS_BUCKET" in
25+
*alb-logs*)
26+
echo "RESULTS_BUCKET ($RESULTS_BUCKET) looks like the ALB access-logs bucket;" \
27+
"use the backend data bucket (pulumi config s3BucketName, e.g. districtr-cdn-data-f1eb3d1)" >&2
28+
exit 1
29+
;;
30+
esac
2331

2432
export AWS_DEFAULT_REGION="$REGION"
2533

backend/stress_test/runner/run.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ RUN_ID="${RUN_ID:?Set RUN_ID}"
1313
SCALE="${SCALE:-0.01}"
1414
WINDOW_SECONDS="${WINDOW_SECONDS:-900}"
1515
RESULTS_BUCKET="${RESULTS_BUCKET:?Set RESULTS_BUCKET to the backend S3 bucket name}"
16+
# Same guard as provision.sh: the ALB access-logs bucket is not writable here.
17+
case "$RESULTS_BUCKET" in
18+
*alb-logs*)
19+
echo "RESULTS_BUCKET ($RESULTS_BUCKET) looks like the ALB access-logs bucket;" \
20+
"use the backend data bucket (e.g. districtr-cdn-data-f1eb3d1)" >&2
21+
exit 1
22+
;;
23+
esac
1624
BASE_URL="${BASE_URL:-https://api.beta.districtr.org}"
1725
CLUSTER="${CLUSTER:-districtr-prod}"
1826
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-2}"

infra/backend.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,24 @@ export function createBackend(
169169
},
170170
});
171171

172+
// The workload is I/O-bound, so CPU stays low while requests queue and the
173+
// policy above never fires. ECS scales out when either policy demands it.
174+
new aws.appautoscaling.Policy(`${name}-backend-req-scaling-policy`, {
175+
policyType: "TargetTrackingScaling",
176+
serviceNamespace: scalingTarget.serviceNamespace,
177+
scalableDimension: scalingTarget.scalableDimension,
178+
resourceId: scalingTarget.resourceId,
179+
targetTrackingScalingPolicyConfiguration: {
180+
predefinedMetricSpecification: {
181+
predefinedMetricType: "ALBRequestCountPerTarget",
182+
resourceLabel: pulumi.interpolate`${alb.alb.arnSuffix}/${alb.backendTargetGroup.arnSuffix}`,
183+
},
184+
targetValue: config.backendRequestsPerTarget,
185+
scaleOutCooldown: 60,
186+
scaleInCooldown: 300,
187+
},
188+
});
189+
172190
return {service};
173191
}
174192

infra/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export const config = {
6060
backendMemory: cfg.getNumber("backendMemory") ?? 8192,
6161
backendMinCount: cfg.getNumber("backendMinCount") ?? (isProd ? 2 : 1),
6262
backendMaxCount: cfg.getNumber("backendMaxCount") ?? (isProd ? 6 : 2),
63+
// ALB requests/min per backend task before scale-out.
64+
backendRequestsPerTarget: cfg.getNumber("backendRequestsPerTarget") ?? 600,
6365
frontendCpu: cfg.getNumber("frontendCpu") ?? (isProd ? 1024 : 512),
6466
frontendMemory: cfg.getNumber("frontendMemory") ?? 2048,
6567
frontendMinCount: cfg.getNumber("frontendMinCount") ?? (isProd ? 2 : 1),

0 commit comments

Comments
 (0)