Retention services

Retention services delete or archive historical data after a retention period. Individual services can be disabled with Retention:*:Enabled=false. Idempotency keys always have a fixed lifetime of 24 hours.

Overview

ServicePurposeDefaultGating
ExecutionRetentionServiceTrims WorkflowExecutions30 dRetention:Executions:Enabled
AuditLogRetentionServiceTrims AuditLogs + gzip/SHA-256 archives365 dRetention:AuditLog:Enabled
WorkflowVersionsRetentionServiceKeeps N versions per workflow50Retention:WorkflowVersions:Enabled
SupportEventRetentionServiceTrims SupportEvents90 dRetention:SupportEvents:Enabled, leader only
NotificationRetentionServiceTrims terminal NotificationDeliveryAttempt records + orphaned NotificationSuppressionState90 dRetention:Notifications:Enabled, leader only
TriggerReceiptRetentionServiceTrims TriggerDeliveryReceipts — one row per observed trigger signal, so the fastest-growing table here. TriggerDeliveryCheckpoints is never swept: one row per trigger node, updated in place7 dRetention:TriggerReceipts:Enabled, leader only
IdempotencyKeyCleanupServiceTrims idempotency keys after a 24 h TTL24 hAlways on (cannot be disabled)

Other background services

ServicePurposeGating
TriggerOrchestrator + QuartzTrigger scan (5 s) + Quartz cron for scheduleTriggerLeader only (in a cluster)
ExecutionDispatchWorkerLeased dispatch of persisted Pending executions from the database outboxLeader-only (in a cluster)
MaintenanceWindowSnapshotServiceKeeps the maintenance-window snapshot per node currentAlways on
WorkflowStatsRefresherComputes the WorkflowStats aggregatesAlways on
ExecutionStatsRollupServiceKeeps the hourly buckets behind the dashboard history current (ExecutionHourlyStat including a duration histogram, FailureCauseHourlyStat); sweeps every 60 s, initial fill in daily chunksStats:Rollup:Enabled (on by default), leader only
DashboardAggregateWarmupPrecomputes the 24 h / 7 d / 30 d dashboard aggregates after startup and after folder changes so the first caller hits the cache tooDashboard:Warmup:Enabled (on by default), per node
RevokedTokensCleanupServiceDaily sweep of RevokedTokensAlways on
HubRevocationSweeperCloses SignalR connections on logout/deactivationAlways on
SupportEventFlushServiceBuffered flush of support events into the databaseAlways on (when the DB projection is enabled)
ClusterLeaderService / ClusterFencingHost / ClusterFailoverRecoveryHostLeader lease, fencing, failover recoveryOnly with Cluster:Enabled

Statistics aggregates

The dashboard and the workflow lists read a precomputed WorkflowStats aggregate instead of scanning WorkflowExecutions per request. It is refreshed by WorkflowStatsRefresher.

KeyDefaultEffect
Stats:RefreshIntervalMinutes5The aggregate refresh interval (1–1440; an unreadable value fails only that pass, and the next one follows after 5 minutes)
Stats:WindowDays7The time window of the aggregated KPIs
Stats:Rollup:EnabledtruePrecomputed hourly buckets for the dashboard history. Turned off, the dashboard recomputes every window from the raw rows — correct, but markedly slower on large histories

The hourly buckets carry the historical dashboard figures (status counts, retry share, duration, failure causes). Median and P95 durations come from histograms and are accurate to within a few percent. Once the initial fill is complete, it covers every window, even when the history is shorter than the window. Live values — running executions, queue depth, heartbeats — are never precomputed. Windows shorter than a day (the 1 h dashboard window) are always computed from the raw executions, because hourly buckets would add up to an hour of runs from before the window. While the initial fill does not yet cover a window, the dashboard keeps serving that window from the live computation. The same happens when the buckets stop being updated — rollup switched off or failing for more than ten minutes — so the dashboard never shows buckets that are missing recent hours. Buckets older than 32 days (the longest dashboard window of 30 days plus a margin) are deleted by the rollup service itself; no separate retention setting exists for them.

GET /api/stats/dashboard returns the state as of the last refresh, not live numbers. Settings mutations write SETTINGS_STATS_UPDATED to the audit log.

All chapters