Description
Add two new fields to RunConfig in pkg/runner/config.go: BackendReplicas *int32 for configuring the desired StatefulSet replica count, and SessionCacheSize int for bounding the local LRU session cache. These fields are the foundation for all horizontal scaling work in the proxyrunner epic — downstream tasks (RC-11, RC-12, RC-13) read these values to control StatefulSet replica management and session cache sizing.
Context
Part of epic stacklok/stacklok-epics#263 — Horizontal Scaling for Proxyrunner (THV-0047). RunConfig is ToolHive's serializable workload configuration and is part of the public API contract. This task extends it with two optional fields that are zero-value-safe: nil BackendReplicas means "don't touch replicas" (preserving HPA/kubectl control), and zero SessionCacheSize will cause consuming code to apply a sensible default of 1000.
Dependencies: None
Blocks: TASK-002 (Configurable StatefulSet replica count), TASK-004 (Implement RoutingStorage)
Acceptance Criteria
Technical Approach
Recommended Implementation
Add the two fields to the RunConfig struct in pkg/runner/config.go, following the existing field ordering and comment conventions. No changes to constructor functions, serialization helpers, or validation logic are needed — the omitempty tags handle backward-compatible serialization, and the zero-value semantics are defined by the consuming code in TASK-002 and TASK-004.
Patterns & Frameworks
- Follow the existing field style in
RunConfig: each field has a Go doc comment, JSON tag, and YAML tag. Fields that are optional use omitempty on both tags.
- Use
*int32 (pointer) for BackendReplicas to allow nil-vs-zero distinction, consistent with how Kubernetes API types represent optional integer fields (e.g., appsv1.StatefulSetSpec.Replicas).
- Use plain
int (not a pointer) for SessionCacheSize since zero is a meaningful sentinel that consuming code maps to a default — consistent with how other integer config fields in the repo are handled.
RunConfig is documented as part of ToolHive's API contract (see the struct comment at line 46); do not change the schema version CurrentSchemaVersion in this PR — these additions are backward-compatible and additive.
Code Pointers
pkg/runner/config.go — The RunConfig struct starting at line 46; add the new fields here, following the comment and tag style of existing fields (e.g., K8sPodTemplatePatch at line 150 is a good nearby example of a Kubernetes-specific optional field with both JSON and YAML tags)
pkg/transport/session/storage.go — The Storage interface; understanding this helps clarify why SessionCacheSize is needed (it bounds the LRU cache used by RoutingStorage in TASK-004)
pkg/container/kubernetes/client.go lines 401–404 — The WithReplicas(1) call that TASK-002 will make conditional on BackendReplicas != nil
Component Interfaces
The two new fields to add to RunConfig:
// BackendReplicas is the desired StatefulSet replica count for the MCP backend.
// When nil, WithReplicas is omitted from the StatefulSet apply spec so that
// HPA or kubectl retains control of the replica count.
BackendReplicas *int32 `json:"backend_replicas,omitempty" yaml:"backend_replicas,omitempty"`
// SessionCacheSize is the maximum number of sessions held in the proxyrunner's
// local LRU cache. When 0, a default of 1000 is used by the consuming code.
SessionCacheSize int `json:"session_cache_size,omitempty" yaml:"session_cache_size,omitempty"`
Testing Strategy
Unit Tests
Integration Tests
Edge Cases
Out of Scope
- Validation logic for
BackendReplicas or SessionCacheSize (e.g., range checks) — deferred to consuming code in TASK-002 and TASK-004
- Wiring
BackendReplicas into the Kubernetes client (TASK-002)
- Wiring
SessionCacheSize into RoutingStorage construction (TASK-004, TASK-006)
- Changes to
CurrentSchemaVersion — these fields are backward-compatible additions
- CLI flags or operator CRD changes to expose these fields — operator/CLI concerns outside this task
References
Description
Add two new fields to
RunConfiginpkg/runner/config.go:BackendReplicas *int32for configuring the desired StatefulSet replica count, andSessionCacheSize intfor bounding the local LRU session cache. These fields are the foundation for all horizontal scaling work in the proxyrunner epic — downstream tasks (RC-11, RC-12, RC-13) read these values to control StatefulSet replica management and session cache sizing.Context
Part of epic stacklok/stacklok-epics#263 — Horizontal Scaling for Proxyrunner (THV-0047).
RunConfigis ToolHive's serializable workload configuration and is part of the public API contract. This task extends it with two optional fields that are zero-value-safe: nilBackendReplicasmeans "don't touch replicas" (preserving HPA/kubectl control), and zeroSessionCacheSizewill cause consuming code to apply a sensible default of 1000.Dependencies: None
Blocks: TASK-002 (Configurable StatefulSet replica count), TASK-004 (Implement RoutingStorage)
Acceptance Criteria
RunConfigstruct inpkg/runner/config.gohas a newBackendReplicas *int32field with JSON tagjson:"backend_replicas,omitempty"and YAML tagyaml:"backend_replicas,omitempty"RunConfigstruct has a newSessionCacheSize intfield with JSON tagjson:"session_cache_size,omitempty"and YAML tagyaml:"session_cache_size,omitempty"BackendReplicasis a pointer type (*int32) so thatnilcan be distinguished from an explicit value of0RunConfigand explain the nil/zero-value semantics for each fieldRunConfigwith both fields set serializes to JSON/YAML and deserializes back with values preservedRunConfigwithout the new fields (omitted) deserializes withBackendReplicas == nilandSessionCacheSize == 0Technical Approach
Recommended Implementation
Add the two fields to the
RunConfigstruct inpkg/runner/config.go, following the existing field ordering and comment conventions. No changes to constructor functions, serialization helpers, or validation logic are needed — theomitemptytags handle backward-compatible serialization, and the zero-value semantics are defined by the consuming code in TASK-002 and TASK-004.Patterns & Frameworks
RunConfig: each field has a Go doc comment, JSON tag, and YAML tag. Fields that are optional useomitemptyon both tags.*int32(pointer) forBackendReplicasto allow nil-vs-zero distinction, consistent with how Kubernetes API types represent optional integer fields (e.g.,appsv1.StatefulSetSpec.Replicas).int(not a pointer) forSessionCacheSizesince zero is a meaningful sentinel that consuming code maps to a default — consistent with how other integer config fields in the repo are handled.RunConfigis documented as part of ToolHive's API contract (see the struct comment at line 46); do not change the schema versionCurrentSchemaVersionin this PR — these additions are backward-compatible and additive.Code Pointers
pkg/runner/config.go— TheRunConfigstruct starting at line 46; add the new fields here, following the comment and tag style of existing fields (e.g.,K8sPodTemplatePatchat line 150 is a good nearby example of a Kubernetes-specific optional field with both JSON and YAML tags)pkg/transport/session/storage.go— TheStorageinterface; understanding this helps clarify whySessionCacheSizeis needed (it bounds the LRU cache used byRoutingStoragein TASK-004)pkg/container/kubernetes/client.golines 401–404 — TheWithReplicas(1)call that TASK-002 will make conditional onBackendReplicas != nilComponent Interfaces
The two new fields to add to
RunConfig:Testing Strategy
Unit Tests
RunConfigwithBackendReplicasset to a non-nil value (e.g.,int32(3)) andSessionCacheSizeset to500; unmarshal the JSON output and assert the fields round-trip correctlyRunConfigthat omits both new fields (i.e., zero-valuedRunConfig{}); assert the JSON output does not containbackend_replicasorsession_cache_sizekeys (omitempty behavior)backend_replicasorsession_cache_size; assertBackendReplicas == nilandSessionCacheSize == 0backend_replicas: 2; assertBackendReplicas != nil && *BackendReplicas == 2Integration Tests
Edge Cases
backend_replicas: 0in JSON — assert this deserializes toBackendReplicaspointing toint32(0), not nil (important:0is an explicit value distinct from omitted)Out of Scope
BackendReplicasorSessionCacheSize(e.g., range checks) — deferred to consuming code in TASK-002 and TASK-004BackendReplicasinto the Kubernetes client (TASK-002)SessionCacheSizeintoRoutingStorageconstruction (TASK-004, TASK-006)CurrentSchemaVersion— these fields are backward-compatible additionsReferences
pkg/runner/config.go—RunConfigstruct (lines 46–213)pkg/container/kubernetes/client.golines 401–404 — downstream consumer ofBackendReplicaspkg/transport/session/storage.go—Storageinterface; context forSessionCacheSize