Class: Karst::Access::Search

Inherits:
Object
  • Object
show all
Defined in:
lib/karst/access/search.rb

Overview

Orchestrates the two-stage search for a user who can actually use a route: the ordinary bounded sample first, then -- only if that found nothing usable -- one bounded retry against each approved candidate population, stopping at the first verified success.

This is deliberately an orchestrator built on top of the existing primitives rather than new behavior inside them. Access::Sweep still owns every actual request (and therefore every rollback, write observation, exception, and halted-callback observation), and CandidatePopulation still owns resolving one configured callable into bounded records. Search only decides what to run next and records what it chose not to run.

Approval boundary: the populations considered here are exactly the ones on the Karst::Access::PrincipalSource objects handed to this class -- which means either explicit configuration (config.principal_populations, or a config.principal_sources :populations entry) or a candidate a developer explicitly approved locally, folded into the same configuration by Karst::Access::ApprovedPopulations. A name merely discovered by Karst::Access::PopulationDiscovery, and never approved, is never executed. Search itself deliberately cannot tell the two apart and never reads approval state: whatever produced this source's populations already had to answer for them. rubocop:disable Metrics/ClassLength

Constant Summary collapse

MAX_RESOLUTION_LIMIT =

Why a population contributed nothing, kept explicit rather than inferred from an empty result so the panel can say what actually happened instead of implying every population was tested:

:usable           ran, and produced a usable outcome
:no_match         ran, and produced no usable outcome
:empty            resolved, but currently matches no records
:already_tried    resolved, but every candidate was already tested
:unresolved       the callable did not yield usable records
:skipped          not tried -- a usable user was already found
:budget_exhausted not tried -- the retry request budget was reached

Hard ceiling on the LIMIT any single population resolution may use, independent of how many users have already been tested. See #resolve_limit.

50
PopulationAttempt =
Value.define(:name, :source_name, :state, :result, :error) do
  def ran?
    !result.nil?
  end
end
Result =

initial is the ordinary Access::Sweep::Result; attempts is one PopulationAttempt per approved population, in configuration order, including the ones deliberately not run. rubocop:disable Metrics/BlockLength

Value.define(:initial, :attempts) do
  def path
    initial.path
  end

  def http_method
    initial.http_method
  end

  # Every outcome observed across both stages, initial sample first.
  def all_outcomes
    ([initial] + attempts.filter_map(&:result)).flat_map(&:outcomes)
  end

  def attempted
    attempts.select(&:ran?)
  end

  def population_request_count
    attempted.sum { |attempt| attempt.result.outcomes.size }
  end

  # The winning evidence and its origin are exposed by Search itself so
  # adapters never need to invent another definition of usable access.
  def verified_outcome
    all_outcomes.find { |outcome| Karst.config.usable_access_outcome.call(outcome) }
  end

  def verified_source
    return nil unless verified_outcome
    return { type: :sample, name: nil } if initial.outcomes.include?(verified_outcome)

    attempt = attempts.find { |item| item.result&.outcomes&.include?(verified_outcome) }
    { type: :population, name: attempt.name }
  end

  def request_count
    initial.outcomes.size + population_request_count
  end

  def elapsed_ms
    ([initial] + attempted.map(&:result)).sum(&:elapsed_ms).round(1)
  end
end

Instance Method Summary collapse

Constructor Details

#initialize(path:, http_method: "GET", sources: nil, application: nil) ⇒ Search

rubocop:enable Metrics/BlockLength



109
110
111
112
113
114
115
116
# File 'lib/karst/access/search.rb', line 109

def initialize(path:, http_method: "GET", sources: nil, application: nil)
  @path = path
  @http_method = http_method
  @sources = sources || {}
  @application = application
  @requests_used = 0
  @tried_keys = {}
end

Instance Method Details

#callObject



118
119
120
121
122
123
# File 'lib/karst/access/search.rb', line 118

def call
  initial = initial_sweep
  return Result.new(initial: initial, attempts: [].freeze) if usable?(initial.outcomes) || approved.empty?

  Result.new(initial: initial, attempts: attempt_populations.freeze)
end