Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve product drive index page performance #4881

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

coalest
Copy link
Collaborator

@coalest coalest commented Dec 21, 2024

Doesn't resolve any ticket.

Description

The product drives index page was mentioned to be slow in production.

Turns out it we were running two aggregate queries for every product drive (for the item quantity and number of unique items).
So listing 10 product drives meant 20 queries just to calculate the totals.
Also this page isn't paginated, so that makes the issue worse. (Although it looks like it used to be?)

Type of change

  • Performance change (should be identical behavior for users)

How Has This Been Tested?

Passes test suite and tested locally.

Screenshots

The performance beforehand scales linearly with number of product drives, so the more i add the worse it got.

These screenshots are when ~10 product drives were being returned.
Before:
Screenshot from 2024-12-11 15-26-51

After:
Screenshot from 2024-12-11 15-27-21

@coalest coalest force-pushed the improve-product-drive-index-page-performance branch from a34bf61 to 596f5a6 Compare December 21, 2024 23:15
@cielf
Copy link
Collaborator

cielf commented Jan 5, 2025

Seems identical in light user experience testing

@coalest
Copy link
Collaborator Author

coalest commented Jan 8, 2025

Link to previous PR: #4845

@cielf cielf requested a review from dorner January 8, 2025 16:32
Copy link
Collaborator

@dorner dorner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the improvement! I had a suggestion for the code organization.

@@ -15,6 +14,11 @@ def index
@item_categories = current_organization.item_categories
@selected_name_filter = filter_params[:by_name]
@selected_item_category = filter_params[:by_item_category_id]
@summary = ProductDriveSummaryService.new(
product_drives: @product_drives,
within_date_range: @selected_date_range,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we're using that date range to get the initial list and then passing it again into the service?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little confusing I know. We are doing two things with this date range:

  1. We only want to show product drives with the given date range.
  2. The totals are reduced by the date range filter (and the item category filter). So we do need to pass in the date range for the total calculations.

So a product drive could overlap with a given date range, but have no donations during that range, so the quanitity/value/item count would all show as zero.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I was previously incorrectly filtering by the date range on the product drives a second time in the service, which indeed didn't make any sense.

@item_category_id = item_category_id
end

def call
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we turn this into a class method rather than requiring an instance? I find it much easier to work with (e.g. for stubbing methods).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

# Returns hash of total quantity, unique item count and value per product drive
# Example return: { 1 => { quantity: 15, unique_item_count: 2, value: 100 }, ...}
#
# @return [Hash<Hash<Symbol, Integer>>]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a Data object instead of a Hash? 

It seems like this is trying to do two things:

  1. Calculate the results
  2. Work with the results that were returned

IMO the service class should just do the calculation with a class method, and return an object that you can use to interrogate the results.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored the class a bit. Now returns a hash of ids mapped to data summary objects. Let me know what you think.

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