Skip to content

multisite: per-site datamachine_* tables are created on wp_initialize_site but never dropped on site deletion (15 orphan c8c_15_* tables left behind) #3517

Description

@chubes4

What happened

On the Extra Chill network a site with blog_id = 15 was registered at 2026-09-17 23:38:20 (site-local; c8c_registration_log row 14, user chubes@extrachill.com, IP 127.0.0.1 → WP-CLI) and deleted shortly after. wp site list no longer shows it and c8c_blogs tops out at 14 (AUTO_INCREMENT = 17, so a blog 16 was also created and removed).

Data Machine's activation path (inc/Core/Bootstrap/ActivationServiceProvider.php:41, add_action( 'wp_initialize_site', on_new_site, 200 )) correctly created the per-site schema at 2026-09-18 03:38:21 UTC. Nothing removed it. Fifteen tables remain with no owning site:

c8c_15_datamachine_batch_items
c8c_15_datamachine_bridge_auth_codes
c8c_15_datamachine_bridge_messages
c8c_15_datamachine_bundle_artifacts
c8c_15_datamachine_flows
c8c_15_datamachine_jobs
c8c_15_datamachine_logs
c8c_15_datamachine_pending_actions
c8c_15_datamachine_pipelines
c8c_15_datamachine_post_identity
c8c_15_datamachine_post_identity_reservations
c8c_15_datamachine_processed_items
c8c_15_datamachine_run_metadata
c8c_15_datamachine_tracked_items
c8c_15_wp_coding_agents_inbound_events      <- wp-coding-agents mu-plugin, same gap

grep -rn "wpmu_drop_tables\|wp_uninitialize_site\|wp_delete_site" inc/ finds only inc/setup/site-md.php:94-96, which lists those hooks for SITE.md recomposition — no code drops tables.

Why it matters

  • WordPress core's site-deletion path (wp_uninitialize_site()wpmu_drop_tables filter) only drops the tables a plugin declares. A network-activated plugin that creates per-site tables and does not filter wpmu_drop_tables (or hook wp_uninitialize_site) leaks its schema on every site deletion.
  • Disposable / test sites are becoming routine on this network (auth.extrachill.com was blog 14 on 2026-09-17; blogs 15 and 16 lived for minutes). Each one leaves ~15 tables.
  • Consumers that iterate prefixes (e.g. the analytics link-page prune cron, which logged Table 'extrachill.c8c_15_extrch_link_page_daily_views' doesn't exist at 03:40:20 UTC, two minutes after the site vanished) trip on the half-deleted state.

Suggested fix (owning layer)

In ActivationServiceProvider (same place wp_initialize_site is hooked), add the symmetric half:

add_filter( 'wpmu_drop_tables', function ( array $tables, int $site_id ) : array {
    return array_merge( $tables, self::table_names_for_site( $site_id ) );
}, 10, 2 );

backed by the same table-name list the installer uses, so the two cannot drift. wp_uninitialize_site() then drops them for free (core already runs DROP TABLE IF EXISTS on that list — see wp-includes/ms-site.php).

Separately, a one-shot cleanup of the existing c8c_15_* orphans is an operator action; noting it here so the fix ships with a way to reconcile pre-existing leaks (e.g. a wp datamachine system orphan-tables --dry-run/--apply).

The wp_coding_agents_inbound_events table is the same gap in the wp-coding-agents mu-plugin; cross-file there if wanted.

Surfaced during the Extra Chill S72 progress review; not fixing from the report layer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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