Connector Tuning
Overview
The Connector reads its sizing and concurrency settings from environment variables on its ECS task
definition. Most of them are set through properties on the frontend block of the Environment
construct.
Two rules apply to every property on this page:
- There are no CDK-side defaults. Omitting a property omits the variable entirely, and the Connector applies its own default. The defaults listed below are the Connector's.
- A misspelt variable name fails silently. The Connector resolves an unknown name to its default and starts normally, so a task definition can look configured while nothing changed. Set these through the CDK properties rather than by hand.
These settings also reach Fargate executor services, which share the Connector's container configuration.
Configuration
import { Environment } from "@stage-tech/depot-cdk";
new Environment(stack, "MyEnvironment", {
account: { name: "example", id: Aws.ACCOUNT_ID },
name: "example",
frontend: {
auroraPool: {
maximumPoolSize: 30,
minimumIdle: 5,
connectionTimeoutMillis: 45000,
},
threadPools: {
dbPoolQueueCapacity: 500,
},
},
});
Every Connector tunable
Where the CDK property column says none, the variable is readable by the Connector but cannot be set from an environment declaration.
Aurora connection pool
frontend.auroraPool.*. Every duration is in milliseconds.
| Environment variable | CDK property | Default | Controls |
|---|---|---|---|
DEPOT_AURORA_POOL_MAXIMUM_POOL_SIZE | auroraPool.maximumPoolSize | 10 | HikariCP maximum pool size |
DEPOT_AURORA_POOL_MINIMUM_IDLE | auroraPool.minimumIdle | 3 | idle connections kept open |
DEPOT_AURORA_POOL_CONNECTION_TIMEOUT | auroraPool.connectionTimeoutMillis | 30000 | wait for a connection before failing |
DEPOT_AURORA_POOL_IDLE_TIMEOUT | auroraPool.idleTimeoutMillis | 60000 | idle connection retirement — inert at the default maxLifetime, see below |
DEPOT_AURORA_POOL_MAX_LIFETIME | auroraPool.maxLifetimeMillis | 30000 | maximum connection lifetime |
DEPOT_AURORA_POOL_VALIDATION_TIMEOUT | auroraPool.validationTimeoutMillis | 5000 | connection validation timeout |
DEPOT_AURORA_POOL_LEAK_DETECTION_THRESHOLD | auroraPool.leakDetectionThresholdMillis | 120000 | log a warning for a connection held this long |
frontend.auroraPoolMaximumPoolSize is the older flat form of auroraPool.maximumPoolSize. It still
works, but setting both throws at synth time — they map to the same variable.
DEPOT_AURORA_POOL_TEST_CONNECTION_COUNT exists too and is deliberately not exposed. It sizes
the test-harness pool factory and has no meaning in a deployed environment; depot-local sets it
directly.
HikariCP overrides two of these silently
HikariConfig.validateNumerics rewrites values it considers inconsistent, warns to the log, and
carries on. Two of the defaults above sit on those rules.
idleTimeout is disabled whenever it is within a second of maxLifetime. At the defaults —
idleTimeout 60000 against maxLifetime 30000 — that condition holds, so idle connections are not
retired on their own schedule at all; they simply reach their maximum lifetime. Lower
idleTimeoutMillis below maxLifetimeMillis if you want idle retirement, or raise
maxLifetimeMillis.
maxLifetime below 30000 is replaced by 1800000, thirty minutes. The default is exactly 30000,
so any reduction crosses that cliff and lands 60× higher than intended rather than lower.
Both rewrites are logged as warnings at pool construction and are otherwise invisible.
Pool size applies per pool
The Connector creates one Aurora pool per distinct connection identity and applies
maximumPoolSize to each. Four distinct Aurora connections at maximumPoolSize: 30 open up to 120
connections, not 30. Size against the database's connection ceiling divided by the number of pools.
Test and run defaults differ
Five of the seven settings above have different defaults in test mode and run mode. The Connector selects between them by inspecting the connection string and username rather than from an explicit setting, and that inference can select test defaults in a deployed environment. The table lists run-mode defaults. An explicitly set property is not subject to the inference.
Thread pools
frontend.threadPools.*.
| Environment variable | CDK property | Default | Controls |
|---|---|---|---|
DEPOT_WORKER_THREADS | threadPools.depotWorker.threads | 0 | depot-worker pool size |
DEPOT_WORKER_QUEUE_CAPACITY | threadPools.depotWorker.queueCapacity | 1000 | depot-worker queue depth, and the basis of its alarm |
DEPOT_SYNC_IO_THREADS | threadPools.syncIo.threads | 8 | depot-syncio pool size |
DEPOT_SYNC_IO_QUEUE_CAPACITY | threadPools.syncIo.queueCapacity | 1000 | depot-syncio queue depth |
DEPOT_DB_POOL_QUEUE_CAPACITY | threadPools.dbPoolQueueCapacity | 0 | queue depth shared by the depot-dbfetch and depot-dbconn pools |
DEPOT_WORKER_THREADS of 0 means derive from the CPU count
Zero is not "no threads": it selects twice the available processor count, which is the value a literal cannot express because it depends on the task size. Omitting the property and setting it to 0 are equivalent. Any positive value wins.
dbPoolQueueCapacity of 0 means no queue
Not a queue of length zero: both JDBC pools get a SynchronousQueue and reject work as soon as
every thread is busy. Their queue.size metrics therefore read 0 whatever the load, and the /echo
saturation gate cannot fire for them. Set a positive value to give them a real queue and make those
metrics meaningful.
Those two pools are sized from DEPOT_AURORA_POOL_MAXIMUM_POOL_SIZE and SNOWFLAKE_MAX_CONCURRENCY
together, at (aurora + snowflake) × 1.1. At the default of 256, Snowflake dominates.
Raise the thread count, not the queue
A queue absorbs a cold start, where several datasets resolve at once. A queue that is persistently
deep rather than spiky means the pool behind it is too small; deepening it only lengthens the wait.
This applies to depot-syncio in particular, whose sizing is unmeasured against real admin traffic.
depot-syncio also drains to zero threads when idle, so a pool size of 0 is healthy rather than a
missing metric.
Query and client limits
| Environment variable | CDK property | Default | Controls |
|---|---|---|---|
SNOWFLAKE_MAX_CONCURRENCY | snowflakeMaxConcurrency | 256 | Snowflake client maximum concurrency |
GRAPHQL_MAX_QUERY_CHARACTERS | graphql.maxQueryCharacters | see GraphQL Query Limits | |
GRAPHQL_MAX_QUERY_TOKENS | graphql.maxQueryTokens | see GraphQL Query Limits | |
GRAPHQL_MAX_WHITESPACE_TOKENS | graphql.maxWhitespaceTokens | see GraphQL Query Limits | |
GRAPHQL_MAX_RULE_DEPTH | graphql.maxRuleDepth | see GraphQL Query Limits | |
GRAPHQL_MAX_QUERY_COMPLEXITY | graphql.maxQueryComplexity | see GraphQL Query Limits | |
GRAPHQL_MAX_QUERY_DEPTH | graphql.maxQueryDepth | see GraphQL Query Limits | |
STAGE_LOG_MIN_SEVERITY | logLevel | — | Connector log level |
REDIRECTING_ENABLED | redirecting | — | redirect responses to work around the 30 s API Gateway cutoff |
Derived from task size
EVENTS_PRIMARY_MIN_THREADS, EVENTS_PRIMARY_MAX_THREADS, EVENTS_SECONDARY_MIN_THREADS and
EVENTS_SECONDARY_MAX_THREADS size the SQS receiver pools. All four take the same value, derived
from frontend.taskSize, and there is no property to override them independently. Minimum equals
maximum, so the receiver pools are fixed-size.
taskSize | Receiver threads |
|---|---|
XSMALL | 1 |
SMALL | 2 |
MEDIUM | 4 |
LARGE, XLARGE | 8 |
| unset | 2 |
Related
- Connector Threading — how to read the thread pool graphs and decide which of these to change.
- Monitoring Dashboards — where the metrics are.
- GraphQL Query Limits — the
frontend.graphqlblock in full.