Class: OnlineMigrations::DataMigrations::SplitSharedAmazonContentSpecRows

Inherits:
OnlineMigrations::DataMigration
  • Object
show all
Defined in:
lib/online_migrations/data_migrations/split_shared_amazon_content_spec_rows.rb

Overview

Clones every shared free-text Amazon content spec row so each item owns
its own. Idempotent: rows already on a single item are skipped.

Background:
These tokens hold per-item marketing copy, but the nightly consolidator
collapsed rows whose text coincided across items. amazon_title_ differentiation was the worst case — 33 of 64 rows shared, covering 460
items, one row shared by 43. While shared, editing one item's copy
silently rewrites every co-tenant's, which is how a single edit to one
towel-warmer's Item Highlight overwrote a sibling SKU's English text on
2026-08-04.

Retiring the consolidator (#1856) stopped NEW sharing but deliberately
left the already-merged rows in place — this migration unpicks them.
It does not set legacy_do_not_merge (renamed from do_not_merge in
#1863): the flag lost its only reader when the consolidator went and the
write helper no longer sets it, so writing it here would be noise on a
column whose name now says it is on the way out.

Only free-text marketing tokens are split. Enum-ish attributes
(amazon_mounting_type, amazon_heating_method_1, …) legitimately share
a row — dozens of items really do all read "Embedded" — and cloning those
would add rows without removing any risk, since a correction there is
meant to apply to the whole set.

Recovery, if a render refresh is lost:
The split and the refresh are not atomic — the refresh is enqueued after
commit, and ItemAttributeWorker runs under the global retry: 0, so a
dispatch or worker failure leaves a split row with a stale
rendered_product_specifications. Re-running this migration will NOT
catch it, because the row is no longer shared and drops out of
#shared_spec_ids. Rendered specs are derived data, so the fix is simply
to re-render the affected items — safe to run at any time:

Item.joins(:product_specifications)
.where(product_specifications: { token: OnlineMigrations::DataMigrations::
SplitSharedAmazonContentSpecRows::TOKENS })
.distinct.find_each(&:update_rendered_product_specifications)

Persisting per-item refresh state was considered and rejected: this is a
one-shot backfill, and the recovery above is cheaper than the bookkeeping.

Defined Under Namespace

Classes: Item, ProductSpecification

Constant Summary collapse

BATCH_SIZE =

Spec rows are heavy on callbacks (translations, render refresh
enqueue), so keep batches small.

25
TOKENS =

Free-text, per-item Amazon marketing copy. Stored as strings to match
the product_specifications.token column type at query time.

%w[
  amazon_title_differentiation
  amazon_title
  amazon_description
  amazon_generic_keyword
  amazon_feature_1
  amazon_feature_2
  amazon_feature_3
  amazon_feature_4
  amazon_feature_5
].freeze

Instance Method Summary collapse

Instance Method Details

#collectionActiveRecord::Batches::BatchEnumerator

Yields batches of shared Amazon content spec rows.

Returns:

  • (ActiveRecord::Batches::BatchEnumerator)

Raises:

  • (ActiveRecord::ActiveRecordError)

    on database error.



99
100
101
# File 'lib/online_migrations/data_migrations/split_shared_amazon_content_spec_rows.rb', line 99

def collection
  ProductSpecification.where(id: shared_spec_ids).in_batches(of: BATCH_SIZE)
end

#countInteger

Number of shared spec rows queued for splitting. Used by
online_migrations for progress reporting.

Returns:

  • (Integer)

Raises:

  • (ActiveRecord::ActiveRecordError)

    on database error.



120
# File 'lib/online_migrations/data_migrations/split_shared_amazon_content_spec_rows.rb', line 120

def count = shared_spec_ids.size

#process(specs) ⇒ void

This method returns an undefined value.

Splits each spec in the batch into per-item rows. Idempotent: a spec
that already has a single item is a no-op.

Parameters:

  • specs (ActiveRecord::Relation<ProductSpecification>)

    batch of
    shared spec rows yielded by #collection.

Raises:

  • (ActiveRecord::ActiveRecordError)

    propagated from
    transaction / save! if a clone fails.



111
112
113
# File 'lib/online_migrations/data_migrations/split_shared_amazon_content_spec_rows.rb', line 111

def process(specs)
  specs.find_each { |spec| split_spec(spec) }
end