Class: GitlabQuality::TestTooling::CodeCoverage::ClickHouse::TestHealthRiskAggregator

Inherits:
Object
  • Object
show all
Includes:
Client
Defined in:
lib/gitlab_quality/test_tooling/code_coverage/click_house/test_health_risk_aggregator.rb

Overview

Runs the daily aggregation that turns code_coverage.test_coverage_per_file rows into a small code_coverage.test_health_risk_per_group summary the dashboard reads.

Hybrid model: this Ruby class is the orchestrator (schedule, error handling, parameter substitution); ClickHouse runs the bitmap math via INSERT ... SELECT from the SQL file shipped alongside.

Constant Summary collapse

SQL_FILE =
File.expand_path('test_health_risk_aggregation.sql', __dir__)
DEFAULT_FULL_CAPTURE_FLOOR =

Per-test capture is sparse between full captures (a full sweep of the suite lands roughly weekly; weekdays contribute only a small delta), so the coverage universe anchors at the most recent day whose capture volume clears this floor instead of a fixed trailing interval. Counted in distinct test files, because the weekend sweep enumerates the whole suite by test file: full sweeps land 22-27k test files, a single sweep bucket ~13k, and delta days at most ~16k even at their historical worst. Source files would not separate the cases: a few thousand tests already touch tens of thousands of source files through app boot, so a healthy delta day could pass a source-file floor and anchor the window at a partial capture.

18_000
DEFAULT_ANCHOR_LOOKBACK =

Far enough back to bridge two missed weekly full captures before giving up, and well inside the source table's 90-day TTL. When no day within the lookback clears the floor, the run writes nothing so the previous snapshot carries forward rather than publishing a denominator built from sparse data.

'21 DAY'
DEFAULT_RISK_WINDOW =
'30 DAY'
DATE_PATTERN =

snapshot_date is YYYY-MM-DD; intervals are <integer> <unit> (singular or plural).

/\A\d{4}-\d{2}-\d{2}\z/
INTERVAL_PATTERN =
/\A\d+\s+(SECOND|MINUTE|HOUR|DAY|WEEK|MONTH|QUARTER|YEAR)S?\z/i

Instance Method Summary collapse

Constructor Details

#initialize(url:, database:, username: nil, password: nil, logger: nil, full_capture_floor: DEFAULT_FULL_CAPTURE_FLOOR, anchor_lookback: DEFAULT_ANCHOR_LOOKBACK, risk_window: DEFAULT_RISK_WINDOW) ⇒ TestHealthRiskAggregator

Returns a new instance of TestHealthRiskAggregator.



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/gitlab_quality/test_tooling/code_coverage/click_house/test_health_risk_aggregator.rb', line 48

def initialize(
  url:, database:, username: nil, password: nil, logger: nil,
  full_capture_floor: DEFAULT_FULL_CAPTURE_FLOOR, anchor_lookback: DEFAULT_ANCHOR_LOOKBACK,
  risk_window: DEFAULT_RISK_WINDOW)
  @url = url
  @database = database
  @username = username
  @password = password
  @logger = logger || ::Logger.new($stdout, level: 1)
  @full_capture_floor = full_capture_floor
  @anchor_lookback = anchor_lookback
  @risk_window = risk_window
end

Instance Method Details

#run(snapshot_date: Date.today) ⇒ void

This method returns an undefined value.

Parameters:

  • snapshot_date (Date, String) (defaults to: Date.today)

    date stamp for this run; defaults to today.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/gitlab_quality/test_tooling/code_coverage/click_house/test_health_risk_aggregator.rb', line 64

def run(snapshot_date: Date.today) # rubocop:disable Metrics/AbcSize
  date = validate_date(snapshot_date)
  coverage_start = find_coverage_start
  if coverage_start.nil?
    logger.warn(
      "#{LOG_PREFIX} No day within #{anchor_lookback} captured >= #{full_capture_floor} " \
        "distinct test files; skipping snapshot for #{date} so the previous row carries forward."
    )
    return
  end

  sql = build_sql(snapshot_date: date, coverage_start: coverage_start)
  logger.info(
    "#{LOG_PREFIX} Running test_health_risk aggregation snapshot_date=#{date} " \
      "coverage_start=#{coverage_start} risk_window=#{risk_window}"
  )
  client.query(sql, format: "TabSeparated")
  report_row_count(date)
rescue StandardError => e
  logger.error("#{LOG_PREFIX} Aggregation failed for #{snapshot_date}: #{e.message}")
  raise
end