Skip to main content

Sizing and scaling

This page gives the capacity of each chart profile, the values that change it, and the database connection budget. The numbers come from the profiles and values.yaml of the chart. They are scheduling requests, not measured peaks. Keep room for the operating system, the cluster and a rolling update.

Profiles at a glance​

ProfileNodesServersRequests of each serverPostgreSQL requests and volumeReviews at onceConversation turns at once
dev.yaml, preview.yamlOne workstation node2250m CPU, 3 GiB memory, 5 GiB Git cache100m CPU, 1 GiB memory, 5 GiB volume0: sandboxes are off0
small.yamlOne node of 4 vCPU, at least 16 GiB of memory and 150 GiB of disk1500m CPU, 3 GiB memory, 20 GiB Git cache250m CPU, 2 GiB memory, 20 GiB volume511, or 7 with backups on (auto)
One host: small.yaml with k3s-values.yamlThe node of small.yaml1As small.yamlAs small.yaml49, or 6 with backups on (auto)
cluster.yamlSeveral nodes, with sandbox nodes that hold the review and conversation quotas2500m CPU, 3 GiB memory, 70 GiB Git cache250m CPU, 2 GiB memory, 20 GiB volume128

Each server's memory request equals its limit, because the JVM sizes its heap from the limit. The server's CPU limit is 4 cores, and 2 with dev.yaml and preview.yaml. The Git cache is node storage of each server (Storage and retention).

The one host has fewer reviews and turns than small.yaml alone, because each gVisor sandbox Pod also takes 48 MiB and 10m CPU of RuntimeClass overhead.

The sandbox quotas hold these requests at full load, with any RuntimeClass overhead:

ProfileReview quotaConversation quota
small.yaml5 Pods: 500m CPU, 2560 MiB memory11 Pods: 1100m CPU, 4224 MiB memory
One host4 Pods: 440m CPU, 2240 MiB memory9 Pods: 990m CPU, 3888 MiB memory
cluster.yaml12 Pods: 1200m CPU, 6144 MiB memory8 Pods: 800m CPU, 3072 MiB memory

Add the webapp (50m CPU, 64 MiB) and the agent image DaemonSet (1m CPU and 16 MiB on each sandbox node). With cluster.yaml, also add the agent-sandbox controller (10m CPU, 64 MiB). During an install or an upgrade, the migration Job requests 250m CPU and 1536 MiB more. Check the rendered requests against the allocatable resources of your nodes before you install.

Raise capacity​

To getChangeAlso needs
More reviews at oncesandbox.reviewMemory: 512 MiB more for each review, plus the RuntimeClass overheadThat memory on the sandbox nodes. Each review also takes about 18 MiB of the server's memory and two database connections of its pool.
More conversation turns at oncesandbox.conversationTurns, or with auto a larger sandbox.budget384 MiB and 100m CPU on the sandbox nodes for each turn (Sandbox capacity budget)
More API capacity, and reception during a restartserver.replicasA server request for each replica, and its database connections. More servers do not add reviews or turns.
Larger repositoriesserver.gitCache.sizeLimitThat much free ephemeral storage on each server's node
More database spacepostgresql.persistence.sizeChange a volume size
A larger review or conversation sandboxsandbox.templates.<kind>.resourcesThe quotas follow the requests (Sandbox sizes)

These signals show that a capacity limit holds work back:

SignalMeaning
agent_queue_oldest_age_seconds, HephaestusAgentQueueStalledReviews wait for a review sandbox.
mentor_turn_queued, mentor_turn_queue_wait_secondsQuestions wait for a conversation turn.
hikaricp_connections_pending, HephaestusDatabaseConnectionTimeoutsServer threads wait for a database connection.
HephaestusSandboxGatewayConnectionsHighThe sandbox gateway is near sandbox.gateway.maxConnections.
HephaestusGitCacheNearlyFullThe Git cache is near server.gitCache.sizeLimit.
llm_pool_permit_wait_secondsCalls wait for the model provider, not for the cluster (Call pool metrics).

PostgreSQL memory​

The bundled image calculates starting settings from its container limit. postgresql.parameters adds explicit -c arguments after that calculation. The small profile uses these values for a shared node:

SettingValue
shared_buffers256MB
work_mem2MB
hash_mem_multiplier1.0
max_connections120, derived from the connection budget
maintenance_work_mem64MB
autovacuum_work_mem32MB
autovacuum_max_workers3
effective_cache_size1GB
max_parallel_workers_per_gather0
max_wal_size1GB

work_mem applies to each sort or hash operation and can multiply across concurrent queries. effective_cache_size is a planner estimate. It allocates no memory. See PostgreSQL resource consumption.

The PostgreSQL container requests its full 2 GiB memory limit. With backups on, its pgBackRest sidecar also requests its full 512 MiB limit. At 120 connections, an allowance of 5 MiB for each backend, the shared buffers, autovacuum, maintenance memory and other memory total about 1512 MiB. Two work operations for each connection bring the estimate to about 1752 MiB. Both estimates fit the 2 GiB limit.

Database connections​

Each chart role has databasePool.maximumPoolSize and databasePool.minimumIdle. With maximumPoolSize: auto, the chart derives the server pool from the caps that it gives the server:

pool = inbox handlers + 2 × reviews at once + 2 × long-load preparations
+ permit maintenance threads + sync reserve + HTTP/control reserve
PartConnectionsSource
Inbox handlers8server.inboxClaimers
Review captures2 for each review at oncesandbox.reviewMemory
Long-load preparations4, with sandboxes onFixed
Permit maintenance2, with sandboxes onFixed
Sync reserve8, for four syncsFixed
HTTP and control reserve4Fixed

The result is 20 for dev.yaml and preview.yaml, 36 for small.yaml, 34 on one host, and 50 for cluster.yaml. An explicit server.databasePool.maximumPoolSize must hold the derived count. The chart refuses a smaller one. A model stream and a sandbox hold no pooled connection. Sandboxes reach the server through the gateway and have no database pool.

The chart derives max_connections for one rolling update, in which every old server can still drain beside its replacement:

Client or reservesmall.yaml
Servers at overlap: twice server.replicas2
Server pools2 × 36 = 72
Five dedicated LISTEN sessions for each server2 × 5 = 10
Migration Job2
PostgreSQL metrics exporter, the monitor role's connection limit2
Backup sessions2
PostgreSQL probes and role setup3
Client total91
superuser_reserved_connections3
Operator margin4
Required slots98
max_connections: rounded up to a multiple of 20, at least 120120

The chart reserves the exporter and backup sessions also when they are off. With cluster.yaml, four servers at overlap with pools of 50 give max_connections of 240. An explicit postgresql.parameters.max_connections must hold the required slots. The chart refuses a smaller one. An external database must hold the same budget beside its own reserved slots (PostgreSQL connection settings).

When all pooled connections are busy, a caller waits up to 45 seconds for one, then gets an error. HephaestusDatabaseConnectionTimeouts fires on such errors.

Memory headroom​

When the JVM starts, the buildpack's memory calculator sets the Java memory from the container's memory limit. The default headroom (BPL_JVM_HEAD_ROOM) is 0 %, and the chart exposes no value for it. Thus, a higher memory limit also gives the heap more memory at the next start. Measure the server's total memory use before you change its memory limit. See the Paketo memory calculator.

Disk​

Budget the node's images, logs and sandbox scratch space separately from the PostgreSQL volume and the Git cache. The sandbox templates bound their ephemeral storage (Sandbox sizes). The small profile needs 150 GiB of disk on its node.