Class: OnlineMigrations::DataMigrations::BackfillShipLabeledAt

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

Overview

Backfill ship_labeled_at on deliveries that reached pending_ship_confirm
(or downstream states) with a NULL timestamp. This happened because the
legacy set_ship_labeled_at used update_attribute in an after_transition
callback — the second save could be silently aborted by Delivery's heavy
before_save :set_proper_shipping_cost callback chain. Going forward,
ship_labeled_at is set in-memory in a before_transition callback so it
persists atomically with the state change. This backfill closes the gap
for ~77k pre-fix rows.

Backfill value: max(shipments.updated_at) for the delivery, falling back
to delivery.updated_at when the delivery has no shipments (e.g. fully
service-only or RMA-empty deliveries). For long-shipped invoiced rows
the proxy is approximate, but those rows never appear as warehouse
candidates anyway — accuracy matters most for pending_ship_confirm
rows where the carrier-pickup-time gate uses this column.

See app/models/delivery.rb (state machine before_transition for
pending_ship_confirm) for the forward-going write path.

Constant Summary collapse

BATCH_SIZE =

Batch size.

5_000
ELIGIBLE_STATES =

Recognised eligible states.

%w[
  pending_carrier_confirm
  pending_ship_confirm
  pending_manifest_completion
  return_labels_complete
  shipped
  invoiced
].freeze

Instance Method Summary collapse

Instance Method Details

#collectionObject



39
40
41
# File 'lib/online_migrations/data_migrations/backfill_ship_labeled_at.rb', line 39

def collection
  Delivery.where(state: ELIGIBLE_STATES, ship_labeled_at: nil).in_batches(of: BATCH_SIZE)
end

#countObject



60
61
62
# File 'lib/online_migrations/data_migrations/backfill_ship_labeled_at.rb', line 60

def count
  Delivery.where(state: ELIGIBLE_STATES, ship_labeled_at: nil).count
end

#process(deliveries) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/online_migrations/data_migrations/backfill_ship_labeled_at.rb', line 43

def process(deliveries)
  delivery_ids = deliveries.pluck(:id)
  return if delivery_ids.empty?

  # Single correlated UPDATE per batch — bypasses every Delivery callback
  # (no validation re-run, no set_proper_shipping_cost recompute, no
  # paper_trail version row) and avoids 2N Rails round-trips. Filtered
  # to ship_labeled_at IS NULL to make the migration idempotent and
  # safe to re-run after partial completion.
  Delivery.where(id: delivery_ids, ship_labeled_at: nil).update_all(<<~SQL.squish)
    ship_labeled_at = COALESCE(
      (SELECT MAX(s.updated_at) FROM shipments s WHERE s.delivery_id = deliveries.id),
      deliveries.updated_at
    )
  SQL
end