Ensure that exceptions in loops identify which element it was

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the exceptions category.

Last Updated: 2021-05-16

I started off with this code as part of a long-ish running piece of background accounting code:

  def find_order(sale_id)
    PaypalTransaction
      .find_by!(sale_id: sale_id)
      .payment.order
  end


sales_ids = [... # thousands of entries]
sales_ids.map(&:find_order)
# long wait...
=> ActiveRecord::RecordNotFound 

This exception gave zero info given about WHICH sale ID caused issues. And given that this was payment code, I sure as heck didn't want to run the damn thing again.

One fix is to bubble up the item in question within the exception by rescuing the opaque exception and raise a specific one:

    def find_order(sale_id)
      PaypalTransaction
        .find_by!(sale_id: sale_id)
        .payment.order
    rescue ActiveRecord::RecordNotFound
      raise MissingPaypalTransactionError, "sale_id: #{sale_id}"
    end

(Another is to divide the subjobs into separate background tasks and use the persisted arguments to these jobs as the background info for what exactly blew up.)