Skip to content

perf: speed up network and console store listings - #114

Merged
roncodes merged 1 commit into
release/v0.4.25from
fix/network-stores-performance
Oct 7, 2026
Merged

roncodes merged 1 commit into
release/v0.4.25from
fix/network-stores-performance

Conversation

@roncodes

@roncodes roncodes commented Oct 6, 2026

Copy link
Copy Markdown
Member

Summary

Listing a Network's stores (GET storefront/v1/stores) and the Console's network stores tab could take minutes. The cause was work done once per store instead of once per request, plus loading the whole network whenever distance sorting was triggered.

Related Issue

Reported from device testing of the Network edition app: the stores list took close to 2 minutes.

Type of Change

  • Bug fix

Implementation Notes

  • Store resource: category, networks, locations and media now pass closures to when(). PHP evaluates when()'s value argument even when the condition is false, so every serialized store lazy-loaded networks and ran the three-query network category lookup.

  • Ratings: both the public network store query and the console store query preload withAvg('reviews', 'rating'). Store::getRatingAttribute() uses the preloaded average when present instead of running one AVG query per store. Values match the previous output: integer or float average, 0 when unrated.

  • Console store list (StoreController::onQueryRecord): eager-loads logo and backdrop, plus networks when network and with_category are requested.

  • Network category lookup: reuses eager-loaded networks, and resolves each network id to its uuid once per process (the mapping never changes).

  • Distance: a missing or unparseable location used to become (0, 0), which:

    • loaded every store and location in the network to sort in PHP,
    • measured distances from (0, 0),
    • and with maximum_distance, filtered out every store.

    Such requests now skip distance processing and keep SQL limit/offset. Requests with a real location behave as before.

  • Migration: index on reviews.subject_uuid, guarded so it can be re-run.

The other major cost was Utils::getCountryCodeByCurrency(), which reloads the countries dataset once per store. That is fixed in fleetbase/core-api#291. Both changes are needed for the full speed-up and can be released independently.

This is based on main (v0.4.24) rather than dev-v0.4.19, which is far behind current main.

Validation

  • Tests
  • Lint
  • Build (backend-only change)
  • Manual validation
New tests:
- store rating uses a preloaded review average without querying reviews
- store network category uses eager loaded networks
- store resolves each network id once
- network stores endpoint ignores distance filters without a usable location
- network stores endpoint preloads each store rating in the store query
- console store queries eager load what the store resource serializes

Suite (PHP 8.4, server/tests excluding Unit/Support/StorefrontTest.php): 585 passed, 0 failed
Coverage (Xdebug): every changed executable line covered; StoreController, NetworkController,
  Resources/Store and Models/Store remain at 100% line coverage
php-cs-fixer --dry-run on all changed files: clean

Local note: on this macOS machine the suite exits silently partway through Unit/Support/StorefrontTest.php, on unmodified main as well. So that file was excluded locally and CI is the reference run. This change doesn't touch it.

Documentation Impact

  • No documentation changes needed

API Reference Impact

  • No API reference changes needed

API reference notes: response shapes are unchanged. Only the no-location distance case changes: maximum_distance without a usable location is now ignored instead of returning no stores.

Risk

  • Run migrations to add the reviews.subject_uuid index.
  • Sorting with a real location still post-processes in PHP. Moving distance into SQL (ST_Distance_Sphere) would make very large networks scale further and is a follow-up.

Listing a network's stores ran several queries per store and loaded the whole
network for distance sorting, so a page of stores could take minutes.

- Store resource: build category, networks, locations and media only when the
  field is requested (when() evaluated its value argument for every store)
- Preload the review average with withAvg() on the public and console store
  queries; the rating accessor uses it instead of one AVG query per store
- Console store list eager-loads logo and backdrop, and networks when the
  network category is requested
- Network category lookup reuses eager-loaded networks and resolves each
  network id once instead of once per store
- Ignore missing or unparseable locations instead of measuring from (0, 0):
  no in-memory sort of every store, and maximum_distance no longer filters
  out every store when no location is sent
- Index reviews.subject_uuid
@codecov

codecov Bot commented Oct 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (62af547) to head (a38e234).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##                main      #114   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
- Complexity      2282      2295   +13     
===========================================
  Files            182       182           
  Lines           9082      9103   +21     
===========================================
+ Hits            9082      9103   +21     
Flag Coverage Δ
backend 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@roncodes roncodes mentioned this pull request Oct 6, 2026
@roncodes
roncodes changed the base branch from main to release/v0.4.25 October 6, 2026 19:14
@roncodes
roncodes merged commit 9181534 into release/v0.4.25 Oct 7, 2026
11 checks passed
@roncodes
roncodes deleted the fix/network-stores-performance branch October 7, 2026 05:32
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.

1 participant