---
title: "Retention settings and report"
description: "What the Drupal server stores per run, how to see it, and how the retention policy is declared."
source: https://flowdrop.io/docs/drupal/retention
site: FlowDrop documentation
---
# Retention settings and report
{/*
Claims on this page were read from apps/fddo (branch feature/26-stable-closeout):
config/install/flowdrop.settings.yml — the retention: group and its defaults
config/schema/flowdrop.schema.yml — flowdrop.retention_policy, flowdrop.retention_policy_age_only
src/Retention/RetentionInfo.php — the declared entity types and their kinds
src/Retention/RetentionInfoInterface.php — KIND_POLICY / KIND_EXISTING_CLEANUP / KIND_TTL
src/Retention/RetentionRow.php — the meaning of each report column
src/Retention/RetentionReport.php — the cached report service
src/Drush/Commands/RetentionCommands.php — flowdrop:retention:report, its fields and caveats
src/Hook/RequirementsHooks.php — the status-report line; "there is no administration page"
src/Hook/CronHooks.php — the daily read-only warm
CHANGELOG.md [2.6.0-alpha2], [2.6.0-alpha3] "Removed: the retention mode slim"
UPGRADE.md "New: data retention settings and report", "Removed: the retention mode slim"
*/}
Every workflow run leaves rows behind: a pipeline, a job per node, and — when the
run came from a chat or the Playground — a session and its messages. FlowDrop
declares what it keeps, measures it, and reports on it.
**Nothing is deleted.** There is no sweeper, no cron cleanup and no purge
command for the governed types. Both retention modes behave identically today;
`delete` is accepted so a later release can honour it without a configuration
migration. Setting a mode changes only what the report says.
## Where the settings live
The policy is configuration on `flowdrop.settings`, under a `retention` group.
There is no administration page for it — read and write it like any other
configuration:
```bash
drush config:get flowdrop.settings retention
drush config:set flowdrop.settings retention.flowdrop_job.max_age 604800
```
The group ships with the module and is stamped onto existing sites by
`flowdrop_post_update_add_retention_settings()`, which writes absent keys only,
so a value you already chose survives.
## What each type declares
One block per governed entity type, each with a `mode` and one or two age
thresholds:
| Key | Default | What it governs |
| --- | --- | --- |
| `retention..mode` | `off` | `off` or `delete`. |
| `retention..max_age` | `2592000` (30 days) | Age in seconds after which a success-like terminal row would become eligible. `0` means every such row. |
| `retention..failure_max_age` | `31536000` (365 days) | The same for failure-like terminal rows. Normally longer: a failed run is the one you still want to read next quarter. |
`` is one of `flowdrop_session`, `flowdrop_pipeline`, `flowdrop_job`,
`flowdrop_session_message` and `state_checkpoint`.
`state_checkpoint` carries no status field, so age is the only criterion there:
it takes `mode` and `max_age`, and has no `failure_max_age`.
**Every type ships `mode: off`**, and `off` is a supported, legitimate state — a
site that keeps every run forever is doing nothing wrong.
### The two modes
- **`off`** — keep everything.
- **`delete`** — accepted by the configuration schema and reserved for a later
release. It validates, and today it behaves exactly as `off`.
A third mode, `slim`, was accepted by an earlier alpha and has been withdrawn.
It is gone from the schema, and `flowdrop_post_update_drop_slim_retention_mode()`
rewrites a stored `slim` to `off` when you run `drush updatedb`. Run the update:
a site that still stores `slim` fails strict configuration validation, so
`drush config:inspect` and configuration imports both complain.
### What is never eligible
Only terminal rows are ever counted as eligible. A row that is `paused`,
`pending`, `running`, `idle`, `awaiting_input` or `interrupted` is not terminal
and no setting makes it eligible.
## Reading the report
```bash
drush flowdrop:retention:report # the cached report
drush flowdrop:retention:report --refresh # recompute every count and size first
```
The alias is `fd:retention:report`. The command is read-only: it deletes
nothing, and since nothing else deletes anything either, **this is the dry run**.
Seven columns are shown by default:
| Column | Field | What it means |
| --- | --- | --- |
| Type | `type` | The entity type id. |
| Mode | `mode` | The configured mode for that type. |
| Rows | `rows` | Total stored rows. |
| Size | `bytes` | Base table plus dedicated field tables, data and index length. Not measured off MySQL/MariaDB. |
| Oldest | `oldest` | Age of the oldest row, in whole days. |
| Eligible (ok) | `eligible_success` | Success-like terminal rows older than `max_age`. |
| Eligible (fail) | `eligible_failure` | Failure-like terminal rows older than `failure_max_age`. |
Five more are available through `--fields`: `kind` (whether FlowDrop governs the
type, another module's cron already sweeps it, or its lifetime is an expiry
stamp), `excluded` (rows held by a non-terminal status, which no policy could
ever match), `reclaim` (bytes pro-rated across the eligible share of rows),
`policy` (what already governs a store FlowDrop does not) and `error` (why a row
could not be measured).
```bash
drush flowdrop:retention:report --fields=type,kind,excluded,reclaim,policy
drush flowdrop:retention:report --format=json
```
A structured format always carries every column as raw numbers — bytes, unix
timestamps, integer counts — so you can do arithmetic on them instead of parsing
`198.7 MB` back into bytes. A dash in the table means *not measured*, which is
not the same as zero.
The report also accounts for the stores FlowDrop does **not** govern —
interrupts, workflow snapshots and memory records — so one place accounts for
everything the module deletes. Those keep their own cron cleanups and TTL, and
their eligibility follows that sweeper's own rules: a cancelled snapshot is
never reported as reclaimable.
## The status report line
Drupal's status report carries a **FlowDrop data retention** line. It is
informational, never a warning, and it only ever reads the cached report —
measuring scans every runtime table, which is never done while rendering a page.
Cron builds the report once a day, a read-only warm that measures and caches and
deletes nothing, and rebuilds it on the next run whenever the cache is empty. So
a cache rebuild costs one cron interval rather than a day. Until the first build
the line reads *Not yet computed* and names
`drush flowdrop:retention:report --refresh`.
This is an operational size-and-age report, **not** a GDPR erasure mechanism.
It makes no claim about personal data and offers no subject-level deletion.