fix(sync): defer audio deletion to VIP cron and shorten DELETE timeout#570
Merged
Conversation
on_trash_post() and on_delete_post() each made a blocking 30s remote DELETE with no async path. Bulk-trashing N posts fired N sequential 30s-worst-case DELETEs in one admin request, a WordPress VIP violation that could hang or fatal the request mid-loop. Mirror the generation design: when is_async_generation_enabled() (VIP), capture the project and content IDs and schedule one background DELETE_AUDIO_CRON_HOOK event. The IDs travel in the event args because the meta is wiped on trash (and the row is gone on delete) before the job runs. Off-VIP, fall back to a synchronous DELETE bounded by a new short DELETE_TIMEOUT (3s) instead of 30s. Parametrize Client::build_args() and call_api() with a timeout and add Client::delete_audio_by_ids(); delete_audio() delegates to it. The timeout is now a variable, so the VIP RemoteRequestTimeout sniff no longer fires and the existing phpcs:ignore is removed (none added). Verified: full phpcs ruleset clean; SyncTest and ClientTest pass (121 tests, 303 assertions, 6 new). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
galbus
marked this pull request as ready for review
July 19, 2026 06:56
gouravkhunger
approved these changes
Jul 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Sync::on_trash_post()(wp_trash_post) andSync::on_delete_post()(before_delete_post) each made a blockingClient::delete_audio()call —wp_remote_request()with'timeout' => 30— with no async/deferred path. Unlike audio generation (deliberately deferred to cron on VIP viais_async_generation_enabled()), deletion always ran inline on the admin request.Bulk-trashing N posts with BeyondWords content fired
wp_trash_postper post → N sequential 30s-worst-case remote DELETEs in a single admin request. Against a slow/unreachable API this easily exceeds VIP request limits and can hang or fatal the request mid-loop, leaving posts partially processed. It's also a VIP platform violation (blocking remote request with a long timeout on a synchronous path).Fix
Mirror the generation design, plus give the DELETE a short timeout.
src/post/class-sync.php— deferred deletion on VIPDELETE_AUDIO_CRON_HOOK(beyondwords_delete_audio), registered unconditionally like the generation hook so a queued event still runs after a config change.on_trash_post()/on_delete_post()now call a privatedelete_audio_for_post_or_defer(). Whenis_async_generation_enabled()(VIP), it captures the project + content IDs and schedules a single background cron event — the IDs travel in the event args because the meta is wiped on trash / the row is gone on permanent delete before the job runs. Off-VIP (unreliable WP-Cron), it falls back to a synchronous DELETE.schedule_audio_deletion()mirrorsschedule_audio_generation()(prefers the VIP wrapper, de-dupes viawp_next_scheduled).Sync::delete_audio_by_ids()is the cron callback.src/api/class-client.php— short, parametrized timeoutREQUEST_TIMEOUT = 30(generation/reads, unchanged) andDELETE_TIMEOUT = 3.build_args()/call_api()take an optional$timeout; DELETEs pass the short one.delete_audio()now delegates to a newdelete_audio_by_ids($project_id, $content_id, $post_id), shared by the sync path and the cron callback. Off-VIP bulk trash is now bounded by 3s per post instead of 30s.Reviewer notes
phpcs:ignoreadded — the existing one was removed. Becausebuild_args()now uses'timeout' => $timeout(a variable, not a literal30), the VIPRemoteRequestTimeoutsniff no longer fires, so itsphpcs:ignoreis gone.is_async_generation_enabled()(and itsbeyondwords_async_generate_audiofilter) intentionally — the underlying question, "is background cron reliable here?", is identical for deletion.beyondwords_delete_contenteditor path andbatch_delete_audio(posts-list bulk action) are out of scope; the former still runs synchronously but now benefits from the shorter DELETE timeout.Verification
.phpcs.xml(WordPress-VIP-Go) ruleset — clean, exit 0.SyncTest+ClientTest— 121 tests, 303 assertions, all pass, including 6 new tests (defer-on-VIP for trash & delete, sync-path-off-VIP assertingDELETE_TIMEOUT,Sync::delete_audio_by_idsdelegation, Client-level short-timeout + missing-ID coverage).🤖 Generated with Claude Code