Cluster ERP runs periodic work in two places: the cron table deploy/cluster-erp.cron of the service user, and background loops that every server worker starts in src/cluster_erp/server/app.py. This page lists both, the lock that keeps a loop to one worker, and the retention passes in app.py and src/cluster_erp/server/control_retention.py. The admin-configured record retention window is explained on the records module page.
Scope and source¶
- Defined in:
deploy/cluster-erp.cron,src/cluster_erp/server/app.py(lifespan,_leader, and the_*_loopfunctions),src/cluster_erp/server/control_retention.py(sweep_control_plane),src/cluster_erp/server/membership.py. - Count: four cron entries (two active, two commented), counted from the file; four background loops and one one-off background task, counted from the
asyncio.create_taskcalls inlifespan(src/cluster_erp/server/app.py:164-194).
Exact cron times of a node are deployment data; the table gives the frequency only.
Cron entries¶
| Entry | Frequency | Command | State | Source |
|---|---|---|---|---|
| Fleet backup | daily | python -m cluster_erp.server.backup_tenants |
active | deploy/cluster-erp.cron:4-7 |
| Membership sweep | daily, after the backup | python -m cluster_erp.server.membership sweep |
active | deploy/cluster-erp.cron:9-11 |
| Cold CSV export | daily | scripts/backup/pg_cold_storage_export.sh |
commented | deploy/cluster-erp.cron:13-19 |
| Base backup | weekly | pg_basebackup with the backup role |
commented | deploy/cluster-erp.cron:21-25 |
install.sh installs the file as the service user's cron table on every run (deploy/install.sh:210). The cron comment calls the membership sweep a second line of defence next to the in-process loop (deploy/cluster-erp.cron:9-10).
Background loops¶
| Loop | Interval | Leader lock name | Databases | Work | Source |
|---|---|---|---|---|---|
_retention_purge_loop |
24 hours | retention_purge |
default and every tenant, suspended included | Log retention, then purge_expired with the record retention window, then the RetentionSweep event |
src/cluster_erp/server/app.py:404-458 |
_subscription_sweep_loop |
24 hours | subscription_sweep |
active tenants | Advances, renews, or expires subscriptions; events after commit | src/cluster_erp/server/app.py:504-544 |
_analytics_summary_loop |
analytics_summary_refresh_seconds; 0 turns it off |
analytics_summary |
active tenants | Refreshes analytics summary cells | src/cluster_erp/server/app.py:461-501 |
_membership_expiry_loop |
24 hours, multi-tenant only | membership_expiry on the control plane |
control plane | Suspends expired memberships, then runs the control-plane retention | src/cluster_erp/server/app.py:372-401 |
_audit_redaction_backfill |
once, at start-up | none | default and every tenant | Redacts the older audit_log backlog in batches |
src/cluster_erp/server/app.py:314-334 |
Each 24-hour loop sleeps first, so it never runs at start-up; the start-up runs its own catch-up passes instead (src/cluster_erp/server/app.py:158-193). One database that fails is logged, and the loop continues with the next database (src/cluster_erp/server/app.py:452-454, src/cluster_erp/server/app.py:538-540).
Start-up passes¶
| Pass | Source |
|---|---|
| Log retention on the default database and every tenant | src/cluster_erp/server/app.py:158-160 |
| Erasure ledger replay | src/cluster_erp/server/app.py:165-168 |
| Membership expiry sweep, multi-tenant only | src/cluster_erp/server/app.py:180-185 |
| Control-plane retention, multi-tenant only | src/cluster_erp/server/app.py:186-193 |
Leader lock¶
_leader takes pg_try_advisory_lock on the database the pass serves, with a key derived by CRC-32 from cluster_erp:<name>. The worker that gets the lock runs the pass; the others skip it. The lock is released at the end of the pass, so a crashed worker never blocks the next one (src/cluster_erp/server/app.py:357-369).
Log retention¶
_daily_log_retention runs on the default database and on every tenant, suspended tenants included (src/cluster_erp/server/app.py:1267-1284). On each database it:
- deletes expired rows of the token blacklist (
src/cluster_erp/server/app.py:1168-1193); - deletes
activity_logrows older thanactivity_log_retention_daysanderror_logrows older thanerror_log_retention_days, in batches oflog_purge_batch_rows(src/cluster_erp/server/app.py:1196-1245); - writes an evidence row of kind
logs, even when nothing was removed (src/cluster_erp/server/app.py:1246-1263).
Control-plane retention¶
sweep_control_plane runs at start-up and daily on a multi-tenant node, and on demand with python -m cluster_erp.server.membership retention (src/cluster_erp/server/membership.py:137-142). Every pass writes one row to control_retention_runs (src/cluster_erp/server/control_retention.py:170-171).
| Step | Window setting | Effect | Source |
|---|---|---|---|
| Rate-limit rows | control_rate_limit_retention_hours |
Deletes rows of control_rate_limits older than the window. |
src/cluster_erp/server/control_retention.py:85-93 |
| Abandoned and cancelled signups | signup_verify_ttl_hours plus control_signup_retention_days |
Deletes unverified queued memberships past the link lifetime plus the window, and cancelled ones past the window. | src/cluster_erp/server/control_retention.py:95-110 |
| Orphaned registrations | none | Soft-deletes a tenant row in provisioning state that has no membership left. |
src/cluster_erp/server/control_retention.py:112-122 |
| Retired registrations | control_signup_retention_days |
Replaces name and mail domains of soft-deleted tenant rows past the window. | src/cluster_erp/server/control_retention.py:124-135 |
| Operator actions | control_actions_retention_years |
Deletes operator action records older than the window. | src/cluster_erp/server/control_retention.py:137-145 |
| Inactive operators | control_actions_retention_years |
Replaces username, password hash, and second-factor secret of long-inactive operator accounts. | src/cluster_erp/server/control_retention.py:147-160 |
| Spooled mails | signup_mail_spool_retention_days |
Deletes spooled verification mail files, only with the spool transport. |
src/cluster_erp/server/control_retention.py:162-168 |
Active or suspended customers, their memberships, and provisioned tenants are not touched by this pass (src/cluster_erp/server/control_retention.py:30-31).
Discrepancies¶
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| leader lock | _leader |
A per-pass advisory lock that lets one worker run a periodic pass while the others skip it. | lider kilidi | src/cluster_erp/server/app.py:357-369 |
| control-plane retention | sweep_control_plane |
The daily deletion and anonymisation pass over the control-plane tables. | kontrol düzlemi saklama geçişi | src/cluster_erp/server/control_retention.py:77-175 |
| retention evidence row | record_run |
The row that records what a retention pass removed. | saklama kanıt satırı | src/cluster_erp/modules/records/retention/services.py:20-26 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.