# frozen_string_literal: true module Gitlab module Usage module Metrics module Aggregates module Sources class PostgresHll extend Calculations::Intersection class << self def calculate_metrics_union(metric_names:, start_date:, end_date:, recorded_at:) time_period = start_date && end_date ? (start_date..end_date) : nil Array(metric_names).each_with_object(Gitlab::Database::PostgresHll::Buckets.new) do |event, buckets| json = read_aggregated_metric(metric_name: event, time_period: time_period, recorded_at: recorded_at) raise UnionNotAvailable, "Union data not available for #{metric_names}" unless json buckets.merge_hash!(Gitlab::Json.parse(json)) end.estimated_distinct_count end def save_aggregated_metrics(metric_name:, time_period:, recorded_at_timestamp:, data:) unless data.is_a? ::Gitlab::Database::PostgresHll::Buckets Gitlab::ErrorTracking.track_and_raise_for_dev_exception(StandardError.new("Unsupported data type: #{data.class}")) return end # Usage Ping report generation for gitlab.com is very long running process # to make sure that saved keys are available at the end of report generation process # lets use triple max generation time keys_expiration = ::Gitlab::UsageData::MAX_GENERATION_TIME_FOR_SAAS * 3 Gitlab::Redis::SharedState.with do |redis| redis.set( redis_key(metric_name: metric_name, time_period: time_period&.values&.first, recorded_at: recorded_at_timestamp), data.to_json, ex: keys_expiration ) end rescue ::Redis::CommandError => e Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e) end private def read_aggregated_metric(metric_name:, time_period:, recorded_at:) Gitlab::Redis::SharedState.with do |redis| redis.get(redis_key(metric_name: metric_name, time_period: time_period, recorded_at: recorded_at)) end end def redis_key(metric_name:, time_period:, recorded_at:) # add timestamp at the end of the key to avoid stale keys if # usage ping job is retried "#{metric_name}_#{time_period_to_human_name(time_period)}-#{recorded_at.to_i}" end def time_period_to_human_name(time_period) return Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME if time_period.blank? start_date = time_period.first.to_date end_date = time_period.last.to_date if (end_date - start_date).to_i > 7 Gitlab::Usage::TimeFrame::TWENTY_EIGHT_DAYS_TIME_FRAME_NAME else Gitlab::Usage::TimeFrame::SEVEN_DAYS_TIME_FRAME_NAME end end end end end end end end end