feat(integrations): contact cron for push and pull#4608
Conversation
There was a problem hiding this comment.
Pull request overview
Implements a WP-Cron–driven “contact cron” system to enqueue and batch-process reader contact pull (from integrations) and push (to integrations), replacing the prior Action Scheduler–driven orchestration used by Contact_Pull.
Changes:
- Added
Contact_Cronto enqueue active users and process pull/push queues every 5 minutes via WP-Cron. - Refactored
Contact_Pullto focus on pulling + retry scheduling (Action Scheduler), with cron owning orchestration. - Updated unit tests to validate queue behavior instead of Action Scheduler async pull scheduling.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit-tests/integrations/class-test-integrations.php | Updates tests to cover enqueue/queue + cron batch processing behavior. |
| includes/reader-activation/sync/class-contact-sync.php | Adds a helper to detect pending sync retries for a user (used to avoid duplicate push work). |
| includes/reader-activation/integrations/class-contact-pull.php | Refactors pull flow; adds Action Scheduler retry hook/backoff and supporting helpers. |
| includes/reader-activation/integrations/class-contact-cron.php | New cron + queue orchestrator for recurring pull/push processing. |
| includes/reader-activation/class-integrations.php | Loads/initializes the new cron orchestrator instead of the prior pull initializer. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix has_pending_retries() to fetch all pending actions (per_page -1) - Fix stale pull not enqueueing for batch on failure - Add pull_sync() error return for cron fallback - Update pull_sync docblock to match behavior - Add deactivation hook and NEWSPACK_CRON_DISABLE support - Only enqueue for pull when not stale; stale failures fall back to queue
|
I have only read the code so far, and I have two questions... maybe easier if we get in a call but let's see: 1 - I don't see any control on the size of the queue. Isn't there a concern that the queue can get too big? Also, many parallel requests writing to the same huge option... And finally, when you start processing the queue, you clear it right away, what if that process times out before it gets to the end of the queue? 2 - Why do we need the backoff retry strategy? Aren't we retrying in 5 minutes anyways? This was a bit confusing to me |
If it's processed every 5 minutes, I don't expect it to get huge.
Good point. This actually relates to what we're talking about in p1774563416311379-slack-C07LB7B14GZ and I'm exploring in #4645. We should have a bulk method in the abstraction, defaulting to a loop of
Not necessarily. If the user is not active in the next 5 minutes, we'll lose the sync. It's safer to rely on the retry and bail if there's a pending retry on the next cron run. |
|
Following our internal conversation, 36ed8cf refactors the queue strategy to use usermeta instead. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Hey @miguelpeixe, good job getting this PR merged! 🎉 Now, the Please check if this PR needs to be included in the "Upcoming Changes" and "Release Notes" doc. If it doesn't, simply remove the label. If it does, please add an entry to our shared document, with screenshots and testing instructions if applicable, then remove the label. Thank you! ❤️ |
# [6.40.0-alpha.1](v6.39.1...v6.40.0-alpha.1) (2026-05-07) ### Bug Fixes * **access-control:** allow `0` member limit values to be shown ([#4670](#4670)) ([b7ea1e3](b7ea1e3)) * **access-control:** allow access rules with `supports_anonymous` to evaluate anonymous readers ([#4695](#4695)) ([3219d63](3219d63)) * **access-control:** bump content rule suggestion limit to 100 ([#4669](#4669)) ([64629eb](64629eb)) * **access-control:** ignore content rules with empty value ([#4675](#4675)) ([c01e97d](c01e97d)) * add `use` statements for clarity ([b0074c0](b0074c0)) * add 30s timeout to v2 invisible token acquisition to prevent hang ([8fd1a17](8fd1a17)) * add isolated flag to v2 invisible widget to prevent interference ([9cfcc30](9cfcc30)) * address potential race condition on multiple registrations with same email ([4fbb990](4fbb990)) * **advertising:** toggle spacing under category autocomplete ([1c65593](1c65593)) * **block-theme:** ensure Overlay Menu block panel opens in editor after template part switch ([#4642](#4642)) ([e45cfd0](e45cfd0)) * **campaigns:** remove wrapper div around add campaign modal input ([#4705](#4705)) ([e04d4af](e04d4af)) * centralize, normalize definition of `$referer` ([e38a94f](e38a94f)) * condition config output on RAS ([22c87af](22c87af)) * condition reCAPTCHA v3 actions on their `ready()` ([a70d5ff](a70d5ff)) * **content-gate:** pass redirectToLayout explicitly on Save ([#4702](#4702)) ([a95cd4d](a95cd4d)) * ensure idempotency by making sure callers get current reader data for logged-in readers ([be59918](be59918)) * gracefully reject calls if essential config is missing ([dd4c46b](dd4c46b)) * **indesign:** exclude rich media blocks from export output ([#4614](#4614)) ([6e45232](6e45232)) * make endpoint available only when RAS is enabled, per Copilot ([18de923](18de923)) * **memberships:** prevent duplicate teams on stripped-meta renewals ([#4661](#4661)) ([84f6c99](84f6c99)) * merge into existing `reader` now that we are out of POC ([3200cc0](3200cc0)) * move grecaptcha.execute inside try block to prevent Promise leak ([f224df8](f224df8)) * move reCAPTCHA behind rate limiting to protect metered service from floods ([0ec69a2](0ec69a2)) * **recaptcha:** skip script registration on TEC Community Events pages ([#4666](#4666)) ([d9a57c5](d9a57c5)) * reject Promise and provide helpful error if reCAPTCHA not happy ([099a109](099a109)) * send nonce, if available, with registration request ([ab57d81](ab57d81)) * textarea support ([8676e2d](8676e2d)) * use returned status rather than hardcoded value ([ea9af30](ea9af30)) * use server-side email, not submitted email, for logged-in users ([56e699f](56e699f)) ### Features * **access-control:** add Specific posts content rule ([#4674](#4674)) ([6735309](6735309)) * **access-control:** auto-signup on renewal only for already subscribed lists ([#4621](#4621)) ([23934c6](23934c6)) * **access-control:** human-readable group subscription names ([#4667](#4667)) ([a817304](a817304)) * **access-control:** tweaks to Access Control UI components ([#4659](#4659)) ([3c08943](3c08943)) * add block theme support to lite site ([#4628](#4628)) ([71632b9](71632b9)) * add overridable registration key methods to Integration base class ([2b4d04f](2b4d04f)) * add reCAPTCHA v2 invisible support to register() ([60b0e77](60b0e77)) * **block-theme:** add size options to the Overlay Menu ([#4652](#4652)) ([3b11b75](3b11b75)) * **block-theme:** move co-authors RSS feed code to the Newspack Plugin ([#4629](#4629)) ([50a160c](50a160c)) * **co-authors:** support CAP core entity store alongside legacy store ([#4673](#4673)) ([b80c49a](b80c49a)) * **components:** addToolbarBackButton util in newspack-components ([#4619](#4619)) ([ec36aa3](ec36aa3)) * **content-gate:** expose institutional IP allowlist endpoint ([#4685](#4685)) ([c6a054a](c6a054a)) * **data-events:** woo transactional events ([#4687](#4687)) ([544d718](544d718)) * delegate key generation and validation to Integration instances ([6aba746](6aba746)) * initial reader registration API rollup from working branch ([0250b2a](0250b2a)) * **integrations:** add activity logs view ([#4671](#4671)) ([fb98062](fb98062)) * **integrations:** add content gate metadata ([#4605](#4605)) ([dcd2a09](dcd2a09)) * **integrations:** contact cron for push and pull ([#4608](#4608)) ([29e6668](29e6668)) * **integrations:** redesign configure view ([#4668](#4668)) ([472dd06](472dd06)) * **integrations:** redesign dashboard with CardFeature grid ([#4665](#4665)) ([d20aae8](d20aae8)) * localize reCAPTCHA site key and version for both v2 and v3 ([56ba6ea](56ba6ea)) * more options for registration ([4c23648](4c23648)) * **newspack-ui:** refactor newsletter signup form with Newspack UI utilities ([#4491](#4491)) ([1859473](1859473)) * **reader-activation:** configure ESP incoming fields from schema ([#4676](#4676)) ([8bc84cc](8bc84cc)) * **reader-activation:** frontend registration API for integrations ([2d7a1bc](2d7a1bc)) * **settings:** Experimental Tools framework (NPPM-2692) ([#4591](#4591)) ([5a300eb](5a300eb)) * trigger a hook and invoke an integration callback when logged in user registers ([167383a](167383a))
|
🎉 This PR is included in version 6.40.0-alpha.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
All Submissions:
Changes proposed in this Pull Request:
Implements a contact cron system for push and pull to integrations, replacing the existing ActionScheduler strategy from
Contact_Pull.The cron runs every 5 minutes, processing a queue of recently active readers.
The push uses the existing sync strategy, which is backed by the retry strategy implemented with AS. The same strategy was added to the contact pull, so failed pulls are also handled.
To avoid piling up failed and duplicate requests, a new
has_pending_retries( $user_id )prevents the push or pull of unresolved requests.How it works:
Contact_Cron::maybe_enqueue_contact()adds the user to both pull and push queues (throttled per-user to once every 5 minutes)Contact_Cron::handle_batch(), which processes both queuesHow to test the changes in this Pull Request:
NEWSPACK_LOG_LEVELconstant set to higher than 1wp cron event run newspack_contact_cron_batch) and verify that the user meta items are cleared, and the contact data is syncednewspack_contact_pull_retryAS actionOther information: