Summary
With AllAtOnce rollout and a backlog-based scale-to-zero autoscaler on the versioned worker Deployments (e.g. KEDA's Temporal scaler keyed per workerDeploymentBuildId, minReplicaCount: 0), a newly rolled-out version can deadlock and never become Current: the autoscaler holds its Deployment at 0 replicas, so it has no pollers; the controller then refuses to promote it; the next deploy supersedes it; and it is left permanently as an INACTIVE / never-Current version. It never self-recovers.
Environment
temporal-worker-controller v1.8.0 (Helm chart 0.27.0), controller-runtime v0.24.0
- Temporal Server v1.30.x, CLI v1.6.x
- Rollout strategy:
AllAtOnce
- Per-version pod autoscaling via a KEDA
ScaledObject (temporal scaler), minReplicaCount: 0, trigger keyed to the version's own workerDeploymentBuildId backlog
The deadlock
- A new build lands → the controller creates the versioned Deployment.
- The autoscaler scales that Deployment on the backlog for that build ID. A version that is not yet Current has no workflows pinned to its build ID → its build-id backlog is
0 → the autoscaler scales it to 0 replicas → 0 pollers.
- The controller (
AllAtOnce) attempts to set it Current and fails:
unable to set current deployment version: proposed current version
'default/example-worker:1.4.0-a1b2' is missing active task queues from the
current version; these would become unversioned if it is set as the current version
A version with 0 pollers polls no task queues, so it is "missing the active task queues from the current version." (This guard is correct — promoting a poller-less version would orphan those task queues as unversioned.)
- Deadlock: no pollers → can't be promoted; not Current → no backlog → the autoscaler grants no pollers.
- The next deploy retargets the controller to a newer build. The stranded version stays never-Current →
INACTIVE forever — nothing ever routes to it, so it never gets pollers and never recovers.
Trigger condition
Deploying while the task queue is idle (no backlog to activate the per-build-id scaler). With steady traffic at deploy time — or a minReplicas ≥ 1 autoscaler — the new version comes up with pollers and promotes normally, so this does not reproduce. It's intermittent and correlates strongly with idle-time deploys (in one environment we see a long tail of consecutive never-Current INACTIVE versions from quiet periods, and none from busy periods).
Impact
Reproduction
- Configure a worker with
AllAtOnce rollout and a per-version KEDA ScaledObject (temporal scaler, minReplicaCount: 0, trigger keyed to the version's workerDeploymentBuildId).
- With no traffic on the task queue, roll out a new build.
- Observe: the new version's Deployment is scaled to 0; the controller logs the "missing active task queues" error on the set-current attempt; the version never becomes Current.
- Roll out another build → the previous one is left
INACTIVE permanently.
Expected behavior
During a rollout the controller should ensure the target version has at least one active poller before/while calling SetWorkerDeploymentCurrentVersion — e.g. warm a poller for the target version (ensure ≥1 ready replica, or wait for readiness) prior to set-current, then hand scaling back to the autoscaler. That keeps scale-to-zero autoscaling compatible with rollout, instead of forcing users onto a permanent minReplicas ≥ 1 (which loses scale-to-zero and keeps drained versions from scaling down during sunset — see also #350).
Workarounds
- Use a
minReplicas ≥ 1 autoscaler (e.g. a plain HPA). Removes the deadlock, but gives up scale-to-zero and holds non-current/drained versions at ≥1 replica until the sunset delete.
- Ensure task-queue traffic at deploy time.
Related
Summary
With
AllAtOncerollout and a backlog-based scale-to-zero autoscaler on the versioned worker Deployments (e.g. KEDA's Temporal scaler keyed perworkerDeploymentBuildId,minReplicaCount: 0), a newly rolled-out version can deadlock and never become Current: the autoscaler holds its Deployment at 0 replicas, so it has no pollers; the controller then refuses to promote it; the next deploy supersedes it; and it is left permanently as anINACTIVE/ never-Current version. It never self-recovers.Environment
temporal-worker-controllerv1.8.0 (Helm chart 0.27.0), controller-runtime v0.24.0AllAtOnceScaledObject(temporalscaler),minReplicaCount: 0, trigger keyed to the version's ownworkerDeploymentBuildIdbacklogThe deadlock
0→ the autoscaler scales it to 0 replicas → 0 pollers.AllAtOnce) attempts to set it Current and fails:INACTIVEforever — nothing ever routes to it, so it never gets pollers and never recovers.Trigger condition
Deploying while the task queue is idle (no backlog to activate the per-build-id scaler). With steady traffic at deploy time — or a
minReplicas ≥ 1autoscaler — the new version comes up with pollers and promotes normally, so this does not reproduce. It's intermittent and correlates strongly with idle-time deploys (in one environment we see a long tail of consecutive never-CurrentINACTIVEversions from quiet periods, and none from busy periods).Impact
INACTIVEversions accumulate towardmatching.maxVersionsInDeployment, adding to the version-record cleanup problem (cf. Controller never deletes Temporal-side Worker Deployment version records during normal rollout (only on CRD deletion) #377).Reproduction
AllAtOncerollout and a per-version KEDAScaledObject(temporalscaler,minReplicaCount: 0, trigger keyed to the version'sworkerDeploymentBuildId).INACTIVEpermanently.Expected behavior
During a rollout the controller should ensure the target version has at least one active poller before/while calling
SetWorkerDeploymentCurrentVersion— e.g. warm a poller for the target version (ensure ≥1 ready replica, or wait for readiness) prior to set-current, then hand scaling back to the autoscaler. That keeps scale-to-zero autoscaling compatible with rollout, instead of forcing users onto a permanentminReplicas ≥ 1(which loses scale-to-zero and keeps drained versions from scaling down during sunset — see also #350).Workarounds
minReplicas ≥ 1autoscaler (e.g. a plain HPA). Removes the deadlock, but gives up scale-to-zero and holds non-current/drained versions at ≥1 replica until the sunset delete.Related
minReplicas ≥ 1workaround).INACTIVEversions add to).