Skip to content

Expose rolling update strategy (maxSurge / maxUnavailable) on Plone backend Deployment #144

Description

@jensens

Summary

The Plone construct in cdk8s-plone renders a Kubernetes Deployment for the Plone backend with the default Kubernetes rolling strategy (maxSurge: 25%, maxUnavailable: 25%). For small replica counts + slow container startup (Plone is ~30-60s to "Ready"), this allows a measurable capacity dip during rollouts. There's no way to override it through the construct's API — users need to reach inside with ApiObject.of(...).addJsonPatch(...) to tune it.

Why it matters

A typical production rollout under the defaults:

  • replicas: 6, maxSurge: 25% → 1-2 extra pods allowed during rollout
  • maxUnavailable: 25% → 1-2 pods allowed down during rollout
  • Plone readiness takes 30-60s; during that window, ready-pod-count drops from 6 to ~4

Combined with a cache layer in front (Varnish), URLs that are never cached (authenticated, POST, uncacheable responses, rare querystring combinations, newly-hashed asset URLs) hit the reduced-capacity backend and see 503s for the rollout's duration. Varnish grace mode covers cached content but can't rescue the cold-request case.

For teams that want zero-capacity-dip rollouts the standard fix is maxUnavailable: 0, maxSurge: 50% (or 100%) — new pods come up fully before old ones retire. The only cost is a temporary overshoot of pod count (and thus memory/CPU scheduling) during the rollout, which is acceptable for most production clusters with headroom.

Current workaround (escape hatch)

import { ApiObject, JsonPatch } from 'cdk8s';
import * as k8s from './imports/k8s';

const plone = new Plone(this, 'plone', { ... });

const backendDeployment = plone.node.findAll().find(c =>
  c instanceof k8s.KubeDeployment && c.node.id.includes('backend')
);
if (backendDeployment) {
  ApiObject.of(backendDeployment).addJsonPatch(
    JsonPatch.replace('/spec/strategy', {
      type: 'RollingUpdate',
      rollingUpdate: { maxUnavailable: 0, maxSurge: '50%' },
    }),
  );
}

Brittle: depends on node.id.includes('backend') finding the right construct, and bypasses type-safety. Every consumer that needs this rebuilds the same workaround.

Proposed API

Add a rollingUpdate (or strategy) option to the backend config in PloneBackendConfig (or wherever pod-level settings live for the backend):

export interface PloneBackendConfig {
  // ... existing fields ...

  /**
   * Rolling update strategy for the backend Deployment.
   * Defaults to Kubernetes defaults (25% surge / 25% unavailable).
   *
   * For zero-capacity-dip rollouts use `{ maxUnavailable: 0, maxSurge: '50%' }`.
   */
  readonly rollingUpdate?: {
    readonly maxSurge?: number | string;
    readonly maxUnavailable?: number | string;
  };
}

Passed straight through to Deployment.spec.strategy.rollingUpdate, with type: 'RollingUpdate' implied.

Alternative more-explicit form: accept the whole DeploymentStrategy object if someone wants type: 'Recreate' (rare but valid for dev/test).

Either shape is a one-line map to the Deployment resource, so the implementation is a small change.

Same thing for the other Deployments

If the construct also emits Deployments for related workloads (httpcache, pgthumbor, tika, tika-worker) the same knob would apply. Could either:

  • Mirror the field on each per-role config block (repetitive but explicit), or
  • Have a single defaultRollingUpdate plus per-role overrides.

For aaf-deployment we would realistically want it on backend and on httpcache (Varnish also ~10s to fully warm up + re-learn cluster peers). Rest are low-traffic enough that default is fine.

Happy to send a PR

If the API shape is acceptable I can prepare a PR — the change is localized and the tests should be straightforward (snapshot the Deployment spec and assert the strategy section).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions