Skip to content

Fix: Growth Alert (Inline) N+1 database queries on WooCommerce shop/archive pages#133

Merged
shuvo7670 merged 1 commit into
WPDevelopers:latestfrom
ZebaAfiaShama:83239-growth-alert-inline-duplicate-queries
Jul 20, 2026
Merged

Fix: Growth Alert (Inline) N+1 database queries on WooCommerce shop/archive pages#133
shuvo7670 merged 1 commit into
WPDevelopers:latestfrom
ZebaAfiaShama:83239-growth-alert-inline-duplicate-queries

Conversation

@ZebaAfiaShama

Copy link
Copy Markdown

Problem

Growth Alert (inline) registers its render callback on woocommerce_after_shop_loop_item and woocommerce_after_shop_loop_item_title, which fire once per product in shop/archive loops. Every callback run called Inline::get_notifications_data(), whose request-level cache was commented out — so the same product-independent data was re-queried from the database for every product on the page (classic N+1). Reported by a client whose shop pages slowed down, with NotificationX\Core\Database->get_posts dominating Query Monitor.

This affects every site running NotificationX Pro with the WooCommerce module enabled — the queries fire even with zero Growth Alert campaigns created.

Fix

Restore request-level memoization in Inline::get_notifications_data() (includes/Features/Inline.php), keyed by $source so different inline sources cannot receive each other's cached data (the likely reason the previous un-keyed cache was disabled). The computed result only varies by $source$id/$settings are only passed to the nx_inline_notifications_data short-circuit filter — so per-request caching is safe. Per-product filtering still happens afterward in the extension render loop.

The old public $notifications_data property keeps its previous shape/behavior for backward compatibility.

Verification (Docker sandbox, WP + WooCommerce 10.9.1, NX 3.2.10 + Pro 3.1.2, 11 products, one active Growth Alert stock campaign)

Database->get_posts total from shop-loop callback
Before 137 132
After 11 6

Rendered Growth Alert markup is byte-identical before/after (verified by diffing the archive page HTML). With no inline campaign, per-page calls drop from 49 to 7.

Card

https://projects.startise.com/wp-admin/admin.php?page=fluent-boards#/boards/19/tasks/83239-Bug-Fix-%7C-Growth-Alert-(Inline

🤖 Generated with Claude Code

Growth Alert (inline) hooks fire once per product on WooCommerce
shop/archive pages, and each run re-queried the database for data
that only varies by source. Restore request-level memoization in
Inline::get_notifications_data(), keyed by source so different
inline sources cannot receive each other's cached data.

Measured on an 11-product archive with one active Growth Alert:
Database->get_posts calls drop from 137 to 11 per page load with
identical rendered output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shuvo7670
shuvo7670 merged commit fb8545a into WPDevelopers:latest Jul 20, 2026
@ShahrearMSf
ShahrearMSf requested a review from shuvo7670 July 20, 2026 12:04
@ShahrearMSf ShahrearMSf self-assigned this Jul 20, 2026
@ShahrearMSf

Copy link
Copy Markdown
Contributor

Found okay — reviewed the fix strictly; it's correct and low-risk.

What it fixes

Growth Alert (inline) hooks woocommerce_after_shop_loop_item / _title, which fire once per product on shop/archive pages. With the request-cache commented out, Inline::get_notifications_data() re-queried the same source-level data for every product — a classic N+1 that fired on every NX Pro + WooCommerce site even with zero Growth Alert campaigns.

Correctness (verified)

  • Cache key is right. Result only varies by $source; $id/$settings are only used by the nx_inline_notifications_data short-circuit filter (evaluated before the cache), so keying by $source alone is safe — different inline sources can't receive each other's data (the collision that got the old un-keyed cache disabled).
  • ✅ Per-product filtering still happens afterward in the extension render loop — output unchanged.
  • ✅ Pure read-path memoization — no behavior change, no security surface. php -l clean, property declared, no leftover/duplicate cache blocks.

⚠️ Release provenance — heads up

This PR merged into latest; its merge commit is not in the release branch shakib-pre-master. The release carries the same fix via a separate commit c13c9ee3 (functionally identical to this PR — only a blank line + PCP phpcs:ignore annotations differ). So the fix is shipping, just not through this PR's commit.

Worth reconciling which branch is canonical — the same fix living on latest (this PR) and shakib-pre-master (c13c9ee3) by two authors is a merge-conflict hazard on Inline.php if latest ever merges into the release line.

cc @shuvo7670 bhai — could you confirm the intended target branch here / whether this PR should be re-recorded against the release?

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.

3 participants