Skip to content

Keep unchanged product links when saving a product - #41704

Open
SamJUK wants to merge 1 commit into
magento:2.4-developfrom
SamJUK:fix/product-links-unchanged-resave
Open

SamJUK wants to merge 1 commit into
magento:2.4-developfrom
SamJUK:fix/product-links-unchanged-resave

Conversation

@SamJUK

@SamJUK SamJUK commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Description (*)

Saving a product deletes all of its product links (related, up-sell, cross-sell, grouped) and inserts them again, even when none of them changed. Product::afterSave() always calls Link\SaveHandler::execute(), which deleted every existing link before saving the product's link set. When no links are passed, that set is lazy-loaded from the database, so it is the same links.

Each save therefore uses new catalog_product_link ids and fires the mview triggers on that table. On a 2.4.7 store receiving frequent product updates over REST, catalog_product_link.link_id had reached about 2.02 billion for 140k rows, and the changelogs fed by those triggers held billions of rows.

  • SaveHandler deletes only the links that are no longer in the product's link set. Kept links go through ProductLinkRepository::save(), which already updates an existing link in place.
  • Link::saveProductLinks() skips attribute values that are already stored. INSERT ... ON DUPLICATE KEY UPDATE uses an auto increment id even when it only updates, so unchanged saves still used up catalog_product_link_attribute_* ids. An omitted attribute is only deleted when a value is stored.

Measured over REST (PUT /V1/products/:sku, indexers on Update by Schedule, a product with 6 links):

Save Before: link ids / attribute ids / changelog rows After
No product_links in the payload 6 / 6 / 71 0 / 0 / 11
The same product_links sent again 6 / 6 / 67 0 / 0 / 7
One link removed, one added 6 / 6 / 67 1 / 1 / 17

The changelog rows left after the change come from the product's own attributes.

link_id is now stable across saves, so code that relied on links being re-created on every save will see fewer deletes and inserts.

Related Pull Requests

#41202 takes the same approach for custom options (#39652).

Fixed Issues (if relevant)

  1. Fixes Saving a product deletes and re-inserts all of its product links #41703

Manual testing scenarios (*)

  1. Set the indexers to Update by Schedule: bin/magento indexer:set-mode schedule.
  2. Create simple products A, B, C and D, and give A the related products B (position 1) and C (position 2).
  3. Note A's links with SELECT link_id, linked_product_id FROM catalog_product_link WHERE product_id = <A's id>, and the row count of catalogsearch_fulltext_cl.
  4. PUT /V1/products/A with {"product": {"sku": "A", "price": 12}}.
  5. Expected: the same link_id values, and no changelog rows from catalog_product_link. Before this change, every link gets a new link_id.
  6. PUT /V1/products/A with product_links for B (position 2) and D. Expected: C's link is removed, D's is added, and B keeps its link_id with position 2.
  7. For a grouped product, change one associated product's qty through product_links. Expected: the new qty is saved and both links keep their link_id.

Questions or comments

Tested on MariaDB 10.6. Not tested on Adobe Commerce (row_id and staging).

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • README.md files for modified modules are updated and included in the pull request if any README.md predefined sections require an update
  • All automated tests passed successfully (all builds are green)

Link\SaveHandler deleted every link of a product before saving the
product's links again, so each product save deleted and re-inserted all
of its related, up-sell, cross-sell and grouped links, even when none of
them changed. Each save consumed new catalog_product_link ids and fired
the mview triggers on that table, adding changelog rows for the catalog
search, catalog rule, attribute, price and stock indexers.

- SaveHandler now deletes only the links missing from the product's new
  link set. Kept links are saved through the link repository, which
  already updates an existing link in place.
- Link::saveProductLinks() skips attribute values (position, grouped
  qty) that are already stored. INSERT ... ON DUPLICATE KEY UPDATE uses
  an auto increment id even when it only updates, so unchanged saves
  still consumed catalog_product_link_attribute_* ids. It also no longer
  sends a DELETE for an omitted attribute that has no stored value.

A save that does not change a product's links now writes nothing to the
catalog_product_link tables. Saves that add, remove, reorder or move
links end with the same links as before.
@SamJUK

SamJUK commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

@magento run all tests

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.

Saving a product deletes and re-inserts all of its product links

1 participant