Skip to content

feat: add self-healing sync queue for failed writes - #4349

Open
faisalahammad wants to merge 1 commit into
10up:developfrom
faisalahammad:fix/4226-self-healing-sync-queue
Open

feat: add self-healing sync queue for failed writes#4349
faisalahammad wants to merge 1 commit into
10up:developfrom
faisalahammad:fix/4226-self-healing-sync-queue

Conversation

@faisalahammad

Copy link
Copy Markdown
Contributor

Summary

When Elasticsearch/OpenSearch is briefly unreachable, write operations (index, delete, bulk index) fail silently. The WordPress database and search index drift out of sync until the next full re-index. This adds a persistent journal of failed writes and a WP-CLI command to replay them once the search server is back.

Fixes #4226

Changes

New: FailedWrites.php

Journal class owning a {prefix}ep_failed_writes table created via dbDelta. Captures failed index/delete operations with dedup by (indexable_slug, object_id). Methods: record(), get_pending(), delete_entries(), count_pending().

elasticpress.php

Bump EP_VERSION to 5.4.0. Instantiate FailedWrites::factory() on plugin load.

Command.php

Add replay-queue subcommand with --limit, --dry-run, --skip-health-check flags. Replays each journal entry by re-indexing or re-deleting, clears on success, records new error on failure.

Before: No way to recover from transient failures without a full re-index.
After: wp elasticpress replay-queue replays captured failures.

Why: Matches the reporter's request for a WP-CLI-based recovery mechanism and avoids the cost of a full re-index.

Indexable.php

Fire ep_after_delete_{slug} action after delete_document() so delete failures are capturable. Index and bulk-index paths already had hooks.

Upgrades.php

5.4.0 upgrade routine creates the journal table via dbDelta on first install and on upgrade.

uninstall.php

Drop the ep_failed_writes table on uninstall. Multisite: each site's table is dropped in the per-site loop.

New: TestFailedWrites.php

13 tests (30 assertions): record inserts, dedup by slug+id, get_pending ordering, delete_entries, count_pending, delete/update failure capture, dry-run CLI, live replay CLI, multisite blog_id capture, table creation on upgrade.

Testing

Test 1: Journal captures failed writes

  1. Point ep_host to an unreachable host
  2. Create or update a post
  3. wp db query "SELECT * FROM wp_ep_failed_writes" shows a row
  4. Updating the same post again still shows one row (dedup works)

Test 2: Replay restores sync

  1. Restore reachable ep_host
  2. wp elasticpress replay-queue shows success with progress bar
  3. Journal table is empty after replay
  4. Document is searchable in ES

Test 3: Dry-run mode

  1. wp elasticpress replay-queue --dry-run shows entries without executing

Test 4: Unit tests

  1. vendor/bin/phpunit --filter TestFailedWrites - OK (13 tests, 30 assertions)
  2. Full suite: 5 pre-existing failures in TestDocuments and TestWooCommerceProduct (reproduce on clean develop, not a regression)

Test 5: Uninstall

  1. wp plugin uninstall elasticpress drops the journal table

Adds a persistent journal of failed Elasticsearch/OpenSearch write
operations (index, delete, bulk index) and a WP-CLI command to replay
them when the search server is reachable again.

New files:
- FailedWrites.php - journal class with dbDelta table, record(), get_pending(),
  delete_entries(), count_pending() methods
- TestFailedWrites.php - 13 tests covering all journal operations

Modified files:
- elasticpress.php - bump version to 5.4.0, instantiate FailedWrites
- Command.php - add replay-queue subcommand with --limit, --dry-run,
  --skip-health-check flags
- Indexable.php - fire ep_after_delete_{slug} hook for delete capture
- Upgrades.php - add 5.4.0 upgrade routine creating the journal table
- uninstall.php - drop the journal table on uninstall

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Implement a Self-Healing Sync Queue (Journal) for transient connection failures

1 participant