Skip to content

Session retention

A SessionRetentionPolicy is a statement of intent. This page explains what turns that statement into deleted or archived sessions, and what happens when nothing does.

The operator does not delete sessions. It validates each policy and writes its spec into a ConfigMap named retention-policy-<policy-name> (key retention.yaml) in the operator’s namespace. A finalizer removes the ConfigMap when the policy is deleted.

The compaction CronJob does the expiry. It mounts the ConfigMap of the policy named by the chart value sessionRetention.compaction.policyName (default default), reads the tier settings, and applies them to every session it scans. The policy carries no workspace or agent binding, so one policy governs the whole install.

flowchart LR
SRP[SessionRetentionPolicy] --> OP[Operator]
OP -->|writes| CM["ConfigMap retention-policy-NAME"]
CM -->|mounted by| CJ[Compaction CronJob]
CJ -->|deletes expired sessions| WARM[(Postgres warm store)]
CJ -->|writes Parquet, purges expired data| COLD[(Cold archive: S3, GCS or Azure)]

The Helm chart creates the default policy from sessionRetention.defaultPolicy when sessionRetention.defaultPolicy.create is true, which is the default. It deploys the CronJob only when sessionRetention.compaction.enabled is true, which defaults to false.

The warm store is the Postgres database session-api writes to. Sessions last updated more than warmStore.retentionDays days ago (7 when the policy sets none) are eligible for compaction.

With cold archive enabled, compaction writes each eligible session, with its full message history, to the object store before it deletes the warm rows. coldArchive.retentionDays then bounds how long the archived data stays. The policy decides whether the job archives; the chart values under sessionRetention.compaction.coldArchive (backend, bucket, region, endpoint, Azure account) say where.

With cold archive disabled, the same run deletes the warm rows and keeps no copy. The compaction job logs a warning saying so at the start of every run, and another when a run destroyed sessions.

With the CronJob disabled, the chart default, no component expires session data. The policy still validates, the ConfigMap is still written, and status.phase still reads Active. All three describe the policy itself and say nothing about whether anything applies it.

The Enforced condition carries the missing signal. The controller lists CronJobs labelled app.kubernetes.io/component: compaction in the operator namespace. It reports True when one exists and False when none does. A failed lookup reports Unknown, because a transient API error is not evidence that retention is off. The condition never fails the reconcile: a policy applied before the CronJob exists is a normal state during installation.

Binding a single policy to the compaction job keeps the model small: one ConfigMap, one reader, one schedule. The cost is that retention cannot differ between workspaces.

Running expiry as a CronJob rather than inside session-api keeps destructive work on a schedule you control, with its own resource limits and retry budget. The cost is that expiry runs only as often as the schedule (chart value sessionRetention.compaction.schedule, default 0 2 * * *), so data can outlive its retention by up to one interval.