Skip to content

Automatic cleanup of old repeater "ready" items #595

Description

@adrianbj

Automatic cleanup of old repeater "ready" items

Short description

ProcessWire creates a hidden + unpublished "ready" item for a repeater each time an edit form renders, and never reaps them. The cleanup routine already exists and is complete, but the only way to trigger it is a checkbox on each field's config screen. On a long lived site the items accumulate indefinitely.

The core source already asks this question, in FieldtypeRepeater/config.php next to the checkbox:

// @todo: should we just do this automatically?
$numOldReady = $fieldtype->countOldReadyPages($this->field);

Why it matters

On a site running since 2022 with 37 repeater fields:

  • 19,197 ready items, of which 18,842 are older than the 3 day threshold countOldReadyPages() already uses
  • That is 33% of all pages on the site
  • One field, icon_grid, had 15,242 ready items against 20 real ones

They are not inert. Repeater deletes cascade through deleteRepeaterPage() with children('include=all'), so the orphans are walked and deleted too. One RepeaterMatrix item whose nested parent held 363 orphans took ~12 seconds to remove, which showed up as gateway timeouts when an editor deleted a few blocks and saved.

What already exists

FieldtypeRepeater::countOldReadyPages($field, $delete = false, $secondsOld = 259200) does the whole job. Nothing calls it with $delete = true except the config screen POST handler.

Requests

  1. Run it automatically, or provide a documented hook that a site can schedule. Given deletion can be slow (see processwire-issues for FieldtypePage::hookPagesDelete), doing it inline during a normal request is probably the wrong place - a LazyCron or a maintenance entry point would fit better.

  2. Bound the existing button. The _deleteOldReady handler calls countOldReadyPages($field, true), which finds and deletes the whole backlog in one request with no set_time_limit() and no limit - unlike the neighbouring "unnecessary pages" handler, which sets set_time_limit(600) and caps at 500 with a "run this again" notice. On a large backlog the button reliably times out, which is exactly when a site most needs it. Applying the same cap and notice would make it usable.

  3. Consider reusing ready items more aggressively. On the site above, created and modified for recent ready items match exactly, meaning no existing ready item is ever reused - a fresh one is minted per render (~50/day across two fields). getNextReadyPage() only reuses an item present in the loaded value, so when a nested repeater's field row is absent the existing pool is invisible to it and a new item is created every time.

Workaround

A CLI script that calls countOldReadyPages($field, true) per field, bounded by count and wall clock so it can run from cron and resume across runs.

Environment

  • ProcessWire 3.0.270, PHP 8.5, MySQL 8 (AWS RDS)
  • 37 repeater / RepeaterMatrix fields, ~56,000 pages

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