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

Fix Custom Fields Handling in Storage for both Orders and Products #14111

Conversation

pmusolino
Copy link
Member

@pmusolino pmusolino commented Oct 8, 2024

Closes #14106

Description

It has been reported that for some users orders does not load peaMlT-Wz. It seems that this can happen when a store has a lot of custom fields for products, so upserting custom fields creates a deadlock on the writer context, delaying the syncing of orders for around 2 minutes.
This update optimizes the handling of custom fields in both OrdersUpsertUseCase and ProductStore. The changes improve performance by streamlining the logic for managing custom fields, introducing batch processing, and eliminating redundant operations.

Summary of changes

  • OrdersUpsertUseCase

    • Removed checks for the existence of custom fields in storageOrder that are not present in readOnlyOrder, directly removing them to simplify the logic.
    • Converted the creation of new custom fields from a loop with appends to a map for efficiency.
    • Implemented batch writing for new custom fields to reduce database calls.
  • ProductStore

    • Improved readability and indentation for better code maintenance.
    • Simplified the logic for removing custom fields that are not in readOnlyProduct by directly using a set lookup.
    • Removed redundant operations for managing custom fields.
    • Introduced batch processing for adding new custom fields, thereby optimizing database interactions.

Steps to reproduce

  1. Login to a store with a lot of products and orders, with a lot of custom fields for each one (more than 100).
  2. Notice that Order List is not loading, if not after a lot of time.
  3. Make sure that in the product detail, the list of custom fields will be loaded correctly (cc @hafizrahman).

Testing information

Tested that in a store with a lot of custom fields for products and orders, and everything loads properly.


  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

Reviewer (or Author, in the case of optional code reviews):

Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement:

  • The PR is small and has a clear, single focus, or a valid explanation is provided in the description. If needed, please request to split it into smaller PRs.
  • Ensure Adequate Unit Test Coverage: The changes are reasonably covered by unit tests or an explanation is provided in the PR description.
  • Manual Testing: The author listed all the tests they ran, including smoke tests when needed (e.g., for refactorings). The reviewer confirmed that the PR works as expected on all devices (phone/tablet) and no regressions are added.

pmusolino and others added 6 commits October 8, 2024 18:11
…ields`

- Update comment to clarify the removal process of custom fields in `handleProductCustomFields`
…ore.swift`

- Creates a new array to store new metadata objects before adding to `storageProduct`
…ad-only product

- Update logic to only handle new custom fields from read-only product
- Streamline logic for removing custom fields that are not in read-only product
- Optimize custom fields handling in OrdersUpsertUseCase.swift
- Replace forEach with a set lookup to improve performance
- Introduce batch writing for new custom fields to reduce database calls
@pmusolino pmusolino added type: bug A confirmed bug. feature: order list Related to the order list. feature: product list Related to the product list. impact: high Impacts a lot of users as reported by our Happiness Engineers. labels Oct 8, 2024
@pmusolino pmusolino added this to the 20.6 ❄️ milestone Oct 8, 2024
@dangermattic
Copy link
Collaborator

dangermattic commented Oct 8, 2024

1 Warning
⚠️ This PR is assigned to the milestone 20.6 ❄️. The due date for this milestone has already passed.
Please assign it to a milestone with a later deadline or check whether the release for this milestone has already been finished.
1 Message
📖 This PR contains changes to RELEASE-NOTES.txt.
Note that these changes won't affect the final version of the release notes as this version is in code freeze.
Please, get in touch with a release manager if you want to update the final release notes.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Oct 8, 2024

WooCommerce iOS📲 You can test the changes from this Pull Request in WooCommerce iOS by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS WooCommerce iOS
Build Numberpr14111-161f7cd
Version20.6
Bundle IDcom.automattic.alpha.woocommerce
Commit161f7cd
App Center BuildWooCommerce - Prototype Builds #11120
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

Copy link
Contributor

@selanthiraiyan selanthiraiyan 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 working on this, Paolo!

I tested this on a store with a large number of custom fields and I validated that orders are loaded as expected. ✅

I will wait for @hafizrahman to test that custom fields are displayed without issues.

Yosemite/Yosemite/Stores/Order/OrdersUpsertUseCase.swift Outdated Show resolved Hide resolved
return newStorageMetaData
}

// Batch writing process of multiple custom fields
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice. 👍

Yosemite/Yosemite/Stores/ProductStore.swift Outdated Show resolved Hide resolved
storage.deleteObject(storageCustomField)
}

// Create `customFields` objects from the `readOnlyOrder`
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should unit tests to OrdersUpsertUseCaseTests to validate that it can handle a large number of MetaData items. Right now, I see only tests that add/remove 1 custom field. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

Implemented here dd9ca23

storage.deleteObject(storageCustomField)
}

// Create `customFields` objects from the `readOnlyProduct`
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to the other comment. I think we should unit tests to validate that it can handle a large number of MetaData items.

Copy link
Member Author

Choose a reason for hiding this comment

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

The test implemented here dd9ca23 should work also for Products

@hafizrahman
Copy link
Contributor

hafizrahman commented Oct 8, 2024

Make sure that in the product detail, the list of custom fields will be loaded correctly (cc @hafizrahman).

At this point in this branch, the Product Details is not able to actually show custom fields yet (only shows entry point), with the logic to show them is in a different branch in #14068

However, I was able to test this by putting a breakpoint inside ProductFormViewController. For each time I'm entering a Product Detail, I can confirm that p viewModel.productModel shows the content of the product and it includes its customFields property properly. Since it is what's used to display the content, I'm fairly confident there's no issue here.

Update: additionally, loading the product list screen itself also works fine. This is tested in both iPad and iPhone in iOS 17.5 Simulator with a site with many orders and products, where the products have many custom fields.

@selanthiraiyan selanthiraiyan self-assigned this Oct 8, 2024
@selanthiraiyan
Copy link
Contributor

@pmusolino Please update the PR description to close the issue #14106

…ducts

- Modify `OrdersUpsertUseCaseTests.swift` to include a new test case for processing 5000 custom fields without deadlock.
@pmusolino
Copy link
Member Author

@selanthiraiyan ready for another check


// Batch writing process of multiple custom fields
if !newStorageMetaDataArray.isEmpty {
storageOrder.addToCustomFields(NSSet(array: newStorageMetaDataArray))
Copy link
Contributor

Choose a reason for hiding this comment

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

@pmusolino Just sharing an observation from some debugging - this line caused the upsert to be triggered twice, with newStorageMetaDataArray containing a different custom field object (different Core Data object ID) each time. It's the expected result of the updated approach in this PR, with pre-existing custom fields being deleted and new custom fields re-added.

Copy link
Contributor

@selanthiraiyan selanthiraiyan Oct 9, 2024

Choose a reason for hiding this comment

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

Thanks, Jaclyn!

From the comment of the failing unit test I wonder whether the updated approach of deleting all the stored custom fields before adding new custom fields will cause any reload or UI issues in screens where we use EntityListener to listen to changes to product or order entities. 🤔

Copy link
Contributor

@selanthiraiyan selanthiraiyan left a comment

Choose a reason for hiding this comment

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

Thanks a lot for working on this @pmusolino and @itsmeichigo! I appreciate the efforts. 🙇

With the new approach of deleting all the MetaData items from Storage we are not doing the "upsert" operation of orders and products. i.e. We are not following the "upsert" behaviour, which is to check the existing items from core data and update them. Instead, we delete all the MetaData items and add them again regardless of whether something changed.

I think we should look for a different solution for the following reasons.

  1. The existing method names and comments expect an upsert operation instead of deleting everything. This is misleading.
  2. Core data will consider that the entity is changed even if we write the same order or product into the storage again. (which triggers callbacks and UI reloads) This is not expected from an "upsert" process.

Unfortunately, I don't have any suggestions currently.

- Update `OrdersUpsertUseCase.swift` to upsert custom fields from `readOnlyOrder`.
- Update `ProductStore.swift` to upsert custom fields from `readOnlyProduct`.
- Remove redundant custom fields from both `storageOrder` and `storageProduct` based on `readOnlyOrder` and `readOnlyProduct`.
@pmusolino
Copy link
Member Author

@selanthiraiyan I have pushed new changes here: 3cc83fd. In this update, MetaData items will not be deleted; instead, there will be just the usual upsert. As a result, the previous unit test in OrderStoreTests will no longer fail. These new changes offer blazing fast performance compared to the previous approach while obtaining the same result. It's just an improvement on how metadata are handled in the main cycle. With these changes, I am no longer able to replicate the issue, and the unit test implemented in this PR still works as expected.
Please take a second look 🙌

Copy link
Contributor

@selanthiraiyan selanthiraiyan left a comment

Choose a reason for hiding this comment

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

This looks great! 👏

I tested it using the store with issues, and the orders load as expected.

DispatchQueue.global(qos: .background).async {
orderUseCase.upsert([order])
productStore.upsertStoredProducts(readOnlyProducts: [product], in: backgroundContext)
backgroundContext.saveIfNeeded()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Indentation update needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

@pmusolino pmusolino merged commit 3077cf7 into release/20.6 Oct 9, 2024
14 checks passed
@pmusolino pmusolino deleted the fix/orders-do-not-load-because-of-deadlock-in-customfields-final branch October 9, 2024 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature: order list Related to the order list. feature: product list Related to the product list. impact: high Impacts a lot of users as reported by our Happiness Engineers. type: bug A confirmed bug.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants