Module: Perfgate::Comparison::Engine

Defined in:
lib/perfgate/comparison/engine.rb

Overview

Builds the schema_version 1 comparison-result document (spec section 14.2) from a baseline run-result and a candidate run-result. This is the seam between the fingerprinting/statistics primitives and the CLI: it never touches storage or exit codes (that's Policy::Engine's job), only produces the structured decision document.

Compatibility is checked first (spec section 15): when the runs are incompatible, no per-workload metric decisions are computed at all -- Baseline never silently compares runs it can't vouch for.

Constant Summary collapse

SCHEMA_VERSION =
1

Class Method Summary collapse

Class Method Details

.build_document(baseline_run, candidate_run, compatibility, workloads) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/perfgate/comparison/engine.rb', line 31

def build_document(baseline_run, candidate_run, compatibility, workloads)
  {
    "schema_version" => SCHEMA_VERSION,
    "baseline_run_id" => baseline_run["run_id"],
    "candidate_run_id" => candidate_run["run_id"],
    "compatibility" => compatibility,
    "decision" => overall_decision(compatibility, workloads),
    "workloads" => workloads,
    "diagnostics" => diagnostics(baseline_run, candidate_run, compatibility)
  }
end

.compare(baseline_run:, candidate_run:, config: Perfgate.configuration) ⇒ Object



24
25
26
27
28
29
# File 'lib/perfgate/comparison/engine.rb', line 24

def compare(baseline_run:, candidate_run:, config: Perfgate.configuration)
  compatibility = evaluate_compatibility(baseline_run, candidate_run, config)
  workloads = workloads_for(compatibility, baseline_run, candidate_run, config)

  build_document(baseline_run, candidate_run, compatibility, workloads)
end

.diagnostics(baseline_run, candidate_run, compatibility) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/perfgate/comparison/engine.rb', line 66

def diagnostics(baseline_run, candidate_run, compatibility)
  diagnostics = []
  diagnostics << { "code" => "empty_baseline", "message" => "baseline run has no workloads" } if
    baseline_run.fetch("workloads", []).empty?
  diagnostics << { "code" => "empty_candidate", "message" => "candidate run has no workloads" } if
    candidate_run.fetch("workloads", []).empty?
  Diagnostics.environment_changed_rules(compatibility).each do |message|
    diagnostics << { "code" => "environment_changed", "message" => message }
  end
  diagnostics
end

.evaluate_compatibility(baseline_run, candidate_run, config) ⇒ Object



49
50
51
52
53
54
# File 'lib/perfgate/comparison/engine.rb', line 49

def evaluate_compatibility(baseline_run, candidate_run, config)
  Fingerprints::Compatibility.evaluate(
    baseline_components: baseline_run.fetch("fingerprint", {}),
    candidate_components: candidate_run.fetch("fingerprint", {}), config: config
  )
end

.overall_decision(compatibility, workloads) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/perfgate/comparison/engine.rb', line 56

def overall_decision(compatibility, workloads)
  return "incompatible" if compatibility["status"] == "incompatible"

  decisions = workloads.map { |w| w["decision"] }
  return "fail" if decisions.include?("fail")
  return "warn" if decisions.intersect?(%w[warn incomparable removed_workload new_workload])

  "pass"
end

.workloads_for(compatibility, baseline_run, candidate_run, config) ⇒ Object



43
44
45
46
47
# File 'lib/perfgate/comparison/engine.rb', line 43

def workloads_for(compatibility, baseline_run, candidate_run, config)
  return [] if compatibility["status"] == "incompatible"

  WorkloadComparison.compare_all(baseline_run, candidate_run, config)
end