Class: Batchwatch::BatchResult

Inherits:
Object
  • Object
show all
Defined in:
lib/batchwatch/job.rb

Overview

The split of a batch into landed / failed / expired (#180).

A real batch is three sets, not one: requests that landed, requests that failed per-request, and requests still outstanding when the 24h expiry hit. Reporting only "completed" loses the last two, which is exactly where silent data loss lives. This object carries the three sets, their counts, and the per-request ids, and - because provider ordering is not guaranteed - is built by mapping on custom_id, never by index.

The measurement rule for a partial job (#180): an expiry is reported with status="expired" (NOT completed, which would pollute p90; NOT failed, which would lose the "queue was slow" signal), a per-request-failed job as completed with the landed count, and a clean job as completed. The client does not invent a fourth reading.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(job, landed_ids:, failed_ids:, expired_ids:, results_by_id: nil) ⇒ BatchResult

Returns a new instance of BatchResult.



639
640
641
642
643
644
645
646
647
648
# File 'lib/batchwatch/job.rb', line 639

def initialize(job, landed_ids:, failed_ids:, expired_ids:,
               results_by_id: nil)
  @job = job
  @landed_ids = landed_ids.dup
  @failed_ids = failed_ids.dup
  @expired_ids = expired_ids.dup
  # The caller's own result objects, keyed by custom_id, for the landed ones
  # - so map-back is a hash lookup, never a positional zip.
  @results_by_id = (results_by_id || {}).dup
end

Instance Attribute Details

#expired_idsObject (readonly)

Returns the value of attribute expired_ids.



637
638
639
# File 'lib/batchwatch/job.rb', line 637

def expired_ids
  @expired_ids
end

#failed_idsObject (readonly)

Returns the value of attribute failed_ids.



637
638
639
# File 'lib/batchwatch/job.rb', line 637

def failed_ids
  @failed_ids
end

#jobObject (readonly)

Returns the value of attribute job.



637
638
639
# File 'lib/batchwatch/job.rb', line 637

def job
  @job
end

#landed_idsObject (readonly)

Returns the value of attribute landed_ids.



637
638
639
# File 'lib/batchwatch/job.rb', line 637

def landed_ids
  @landed_ids
end

#results_by_idObject (readonly)

Returns the value of attribute results_by_id.



637
638
639
# File 'lib/batchwatch/job.rb', line 637

def results_by_id
  @results_by_id
end

Class Method Details

.from_handle(job) ⇒ Object

Coarse split from the whole-batch terminal status. For a provider that gives us no per-request lines: an expired handle is all-expired, a non-success terminal handle all-failed, a success all-landed. There are no ids to list at this granularity, so the sets carry a single whole-batch marker each and the counts read 1/0/0-style.



729
730
731
732
733
734
735
736
737
738
739
# File 'lib/batchwatch/job.rb', line 729

def self.from_handle(job)
  handle = job.handle
  if Batchwatch.default_expired(handle) || job.expired
    return new(job, landed_ids: [], failed_ids: [], expired_ids: [WHOLE_MARKER])
  end
  if Batchwatch.default_succeeded(handle)
    return new(job, landed_ids: [WHOLE_MARKER], failed_ids: [], expired_ids: [])
  end

  new(job, landed_ids: [], failed_ids: [WHOLE_MARKER], expired_ids: [])
end

.from_results(job, results = nil, custom_ids = nil) ⇒ Object

Build the split from per-request outcomes, or from the handle status.

See BatchJob#split for the argument contract. The per-request path classifies each result line by whether it carries an error, treats any submitted id with no line as expired, and keys the landed results by custom_id. The handle-status fallback reads the whole-batch terminal state for a provider that reports no lines.



689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/batchwatch/job.rb', line 689

def self.from_results(job, results = nil, custom_ids = nil)
  return from_handle(job) if results.nil? && custom_ids.nil?

  results = Array(results)
  seen = {}
  landed = []
  failed = []
  results.each do |item|
    cid = Batchwatch.custom_id_of(item)
    if cid.nil?
      # A line we cannot map is not silently dropped into "landed" - that
      # would be the zip-by-index mistake in another guise. It counts as
      # failed so the caller sees the reconciliation gap.
      failed << UNMAPPED_MARKER
      next
    end
    seen[cid] = item
    if Batchwatch.result_error?(item)
      failed << cid
    else
      landed << cid
    end
  end

  # Anything submitted but never returned is outstanding at expiry.
   = custom_ids.nil? ? seen.keys : Array(custom_ids)
  expired = .reject { |cid| seen.key?(cid) }

  results_by_id = {}
  landed.each { |cid| results_by_id[cid] = seen[cid] unless cid == UNMAPPED_MARKER }
  new(job, landed_ids: landed.reject { |c| c == UNMAPPED_MARKER },
           failed_ids: failed, expired_ids: expired,
           results_by_id: results_by_id)
end

Instance Method Details

#complete?Boolean

true only when every submitted request landed. No silent success: a job with anything failed or outstanding at expiry is NOT complete. This is the method a caller checks before treating the batch as done.

Returns:

  • (Boolean)


671
672
673
# File 'lib/batchwatch/job.rb', line 671

def complete?
  total > 0 && failed.zero? && expired.zero?
end

#expiredObject



660
661
662
# File 'lib/batchwatch/job.rb', line 660

def expired
  @expired_ids.length
end

#failedObject



656
657
658
# File 'lib/batchwatch/job.rb', line 656

def failed
  @failed_ids.length
end

#landedObject

---- counts, so a caller can branch without .length on three arrays -------



652
653
654
# File 'lib/batchwatch/job.rb', line 652

def landed
  @landed_ids.length
end

#result_for(custom_id) ⇒ Object

The caller's own landed result for one custom_id, or nil. The map-back the naive zip gets wrong: a lookup by id, so out-of-order provider results still reach the right caller object.



678
679
680
# File 'lib/batchwatch/job.rb', line 678

def result_for(custom_id)
  @results_by_id[custom_id]
end

#totalObject



664
665
666
# File 'lib/batchwatch/job.rb', line 664

def total
  landed + failed + expired
end