May 29, 2020·5 min read·engineeringcassandrakubernetesoperatorsrecovery

Cass Operator 1.2 can recover a stalled configuration rollout

Cass Operator 1.2 adds a rack-specific recovery path for broken configuration changes. Its implementation shows why database controllers need a way to apply corrections while nodes are unhealthy.

A database controller can prevent its own recovery when it requires healthy nodes before applying a configuration correction. A bad image reference gives a concrete example. The controller starts rolling out the change, a replacement pod cannot start, and the rollout stops with an unhealthy member. Restoring the intended image in the custom resource is insufficient if reconciliation still waits for the cluster to become healthy before updating its workload configuration.

The DataStax Kubernetes Operator for Apache Cassandra addresses this case in the May 29 release commit for v1.2.0. Its versioned changelog describes the addition as the ability to roll back a broken upgrade or configuration change. The implementation makes that description more precise. An administrator can request that named racks receive the latest StatefulSet configuration even when Cassandra nodes are down.

I am interested in Cassandra because of the difficult availability requirements it can address. That makes recovery behavior a more useful measure of a Kubernetes operator than the brevity of its installation instructions. A controller managing a database needs an explicit answer for a change that prevents the database from becoming ready.

A recovery request attached to the datacenter

The recovery implementation adds forceUpgradeRacks to CassandraDatacenterSpec. Its value is a list of rack names. For each matching rack, the controller constructs the desired StatefulSet from the current datacenter specification and updates the existing StatefulSet. This permits a correction to reach the workload despite the unhealthy nodes that would otherwise obstruct progress.

The distinction between the desired configuration and the recovery request matters. forceUpgradeRacks does not contain an earlier image, a previous configuration, or a saved revision. The administrator first changes the datacenter specification to the configuration that should run. The rack list then requests application of that configuration under exceptional conditions. Calling this rollback is reasonable when the desired specification restores an earlier configuration, but the mechanism does not select that configuration itself.

The integration test in the same commit demonstrates the sequence. It creates a datacenter with three nodes across three racks, sets serverImage to an invalid image reference, and waits until only two pods are ready. It then clears the image override and requests recovery for rack r1. The merge-patch documents used by the test are equivalent to these two successive updates to an existing datacenter:

{"spec":{"serverImage":""}}
{"spec":{"forceUpgradeRacks":["r1"]}}

Here r1 is the affected rack in the test, not a universal rack name. Clearing serverImage restores the operator's normal image selection in this example. A configuration failure needs its own corrected specification before the recovery request. The test then waits for datacenter readiness, so its expected outcome includes recovery of the managed cluster.

What the controller actually changes

The recovery function retains the existing StatefulSet replica count when it builds the replacement specification. Its constructor normally creates a StatefulSet with zero replicas, so copying its result without restoring that count would change the scale of the rack. Existing labels and annotations also enter the desired metadata through a merge. Those details constrain the recovery operation to applying the intended workload configuration while preserving information already attached to the rack's StatefulSet.

Before updating the StatefulSet, the controller records an event naming the rack and sets the datacenter's Updating condition. It also sets operator progress to Updating. After processing the requested racks, it clears forceUpgradeRacks from the datacenter specification. The field therefore represents a request the controller consumes, rather than a permanent preference to ignore unhealthy nodes during every future change.

That behavior has consequences for automation around the custom resource. A management system should understand why this field disappears after reconciliation. Repeatedly restoring the same nonempty rack list would repeatedly submit the exceptional request. Ordinary configuration belongs in the persistent desired specification, while the rack list asks the controller to perform a particular recovery action.

This is recovery of the workload configuration. The code does not restore Cassandra data or reverse a storage-format change. Its evidence is the generated StatefulSet and the integration test with an invalid image. Extending that evidence into a promise that arbitrary Cassandra version downgrades are safe would assign the feature guarantees it does not implement.

Status becomes useful to other controllers

Version 1.2 also adds datacenter status conditions. The implementation records a transition time when a condition changes status. Its tests check that a configuration change sets Updating to true and later returns it to false. Separate tests cover conditions for rolling restarts and scaling operations.

The connection to recovery is practical. A controller that only exposes a broad progress value gives surrounding automation little information about which operation is underway. Named conditions permit a consumer to inspect the datacenter's reported state directly. The release's recovery function uses the same status mechanism before applying its forced StatefulSet update, so the exceptional operation remains visible through the custom resource.

Conditions also help distinguish an accepted specification from a completed operation. The test for configuration changes waits for Updating to become true, then false, before checking that operator progress returns to Ready. That ordering expresses an operational expectation a deployment tool can understand. A successful write to the Kubernetes API is only the start of the change.

The operator's responsibility remains bounded

The v1.2.0 README describes database-aware behavior beyond creating pods. It lists controlled token-ring initialization, seed management, and replacement of unrecoverable nodes. It also explicitly lists integrated repair and integrated backup and restore as unsupported Cassandra features. Those omissions remain relevant when evaluating what this release can operate on an application's behalf.

I find the rack-specific recovery request a useful design choice because it exposes the administrator's intent in the same resource that defines the datacenter. The scope is inspectable, and the controller clears the request after processing it. The feature supplies a concrete intervention when the normal rollout procedure cannot apply a correction.

For a Cassandra deployment, that is a material improvement in the operator's handling of failed changes. The integration test supplies an especially useful example of the supported case, with a broken image override, an explicit correction, and a named rack that must receive it. Backup restoration and database-version compatibility still require their own mechanisms and evidence, as the release's documented boundaries make clear.