Skip to content

Fix PromotionMigrator duplicate codes when promotions share base_code#6422

Open
NewAlexandria wants to merge 2 commits intosolidusio:mainfrom
suvie-eng:fix/promotion-migrator-duplicate-codes-join
Open

Fix PromotionMigrator duplicate codes when promotions share base_code#6422
NewAlexandria wants to merge 2 commits intosolidusio:mainfrom
suvie-eng:fix/promotion-migrator-duplicate-codes-join

Conversation

@NewAlexandria
Copy link

@NewAlexandria NewAlexandria commented Mar 2, 2026

Hi — we ran into this during our promotion migration and wanted to see if this would contribute the fix back.

We may be missing something about the intended behavior, so would appreciate a sanity check. Thanks for your time, of course.

I also added promotions/docs/PROMOTION_MIGRATOR_DUPLICATE_CODES_FIX.md with some RCA context, where we saw it, and version info.

@mrshahsuvie FYI

(an llm summary follows)


Summary

Fixes PG::UniqueViolation during promotion migration when multiple promotions have batches with the same base_code.

Problem

copy_promotion_codes joins to solidus_promotions_promotion_code_batches on base_code only. If two promotions have batches with the same base code, that join can produce multiple rows per legacy code and attempt duplicate value inserts into solidus_promotions_promotion_codes.

Solution

Constrain the batch join by:

  • promotion_id = new_promotion.id
  • created_at = spree_promotion_code_batches.created_at

This prevents cross-promotion row multiplication while preserving expected batch matching.

Testing

  • Added regression coverage in promotions/spec/lib/solidus_promotions/promotion_migrator_spec.rb for two promotions sharing the same batch base_code.
  • Verified migrated code values are inserted exactly once.
  • Ran:
    • bundle exec rspec spec/lib/solidus_promotions/promotion_migrator_spec.rb
    • bundle exec rspec (promotions engine suite)

@NewAlexandria NewAlexandria requested a review from a team as a code owner March 2, 2026 20:24
@github-actions github-actions bot added the changelog:solidus_promotions Changes to the solidus_promotions gem label Mar 2, 2026
@@ -0,0 +1,60 @@
# PromotionMigrator duplicate code fix
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 this documentation should be the commit message, rather than be in the docs folder.

Copy link
Author

Choose a reason for hiding this comment

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

I prompted the change. Please let me know if this is what you're thinking or if you wanted the exact text/the whole file. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please rebase the change into a single commit. Thank you!

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.51%. Comparing base (aa11fbd) to head (f5a5aa4).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6422      +/-   ##
==========================================
+ Coverage   89.50%   89.51%   +0.01%     
==========================================
  Files         981      981              
  Lines       20477    20504      +27     
==========================================
+ Hits        18328    18355      +27     
  Misses       2149     2149              

☔ View full report in Codecov by Sentry.
📢 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.

Constrain the promotion code batch join in copy_promotion_codes by promotion_id
and created_at to prevent cross-promotion row multiplication and duplicate value
inserts. Adds a regression spec and an audit doc summarizing root cause,
release verification, and related issue/PR research.
@mamhoff mamhoff force-pushed the fix/promotion-migrator-duplicate-codes-join branch from 6280b82 to 59d2748 Compare March 11, 2026 09:53
…ase_code

During migration from solidus_legacy_promotions to solidus_promotions,
PromotionMigrator#copy_promotion_codes joins legacy code batches to new
code batches. Previously the join matched only on base_code, which is not
globally unique across promotions. Multiple promotions with batches sharing
the same base_code could multiply rows in the INSERT ... SELECT and attempt
to insert duplicate values into solidus_promotions_promotion_codes, causing
PG::UniqueViolation on index_solidus_promotions_promotion_codes_on_value.

Constrain the LEFT OUTER JOIN on solidus_promotions_promotion_code_batches
with promotion_id = #{new_promotion.id} and created_at equal to the source
batch so each migrated code row resolves to the correct batch for the
promotion being migrated.

Validation: regression spec covers two legacy promotions with PromotionCodeBatch
records sharing the same base_code; migration inserts each code once.

Related research: searched solidusio/solidus issues/PRs for PromotionMigrator,
copy_promotion_codes, base_code, and duplicate-key migration failures; no
prior fix for this join. Issue solidusio#1248 concerns an older spree_promotions.code
migration path and is not the same bug.

Reviewer follow-up: move narrative out of docs/ into this message; drop the
unnecessary Integer() cast on new_promotion.id in the SQL interpolation.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog:solidus_promotions Changes to the solidus_promotions gem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants