Commit 36593d92 authored by Alina Mihaila's avatar Alina Mihaila Committed by Alper Akgun

Add options arguments to Redis HLL Metric YAML definition for filtering data...

Add options arguments to Redis HLL Metric YAML definition for filtering data in instrumentation class
parent de3e1661
---
title: Add options events to Redis HLL metrics for filtering data
merge_request: 61685
author:
type: other
...@@ -9,7 +9,10 @@ value_type: number ...@@ -9,7 +9,10 @@ value_type: number
status: data_available status: data_available
time_frame: 28d time_frame: 28d
data_source: redis_hll data_source: redis_hll
instrumentation_class: CountUsersUsingApproveQuickActionMetric instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_approve
distribution: distribution:
- ce - ce
- ee - ee
......
...@@ -9,7 +9,10 @@ value_type: number ...@@ -9,7 +9,10 @@ value_type: number
status: data_available status: data_available
time_frame: 7d time_frame: 7d
data_source: redis_hll data_source: redis_hll
instrumentation_class: CountUsersUsingApproveQuickActionMetric instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_approve
distribution: distribution:
- ce - ce
- ee - ee
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
"introduced_by_url": { "introduced_by_url": {
"type": ["string", "null"] "type": ["string", "null"]
}, },
"extra": { "options": {
"type": "object" "type": "object"
}, },
"time_frame": { "time_frame": {
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
}, },
"instrumentation_class": { "instrumentation_class": {
"type": "string", "type": "string",
"pattern": "^(([A-Z][a-z]+)+::)*(([A-Z][a-z]+)+)$" "pattern": "^(([A-Z][a-z]+)+::)*(([A-Z]+[a-z]+)+)$"
}, },
"distribution": { "distribution": {
"type": "array", "type": "array",
......
...@@ -43,7 +43,7 @@ Each metric is defined in a separate YAML file consisting of a number of fields: ...@@ -43,7 +43,7 @@ Each metric is defined in a separate YAML file consisting of a number of fields:
| `milestone` | no | The milestone when the metric is introduced. | | `milestone` | no | The milestone when the metric is introduced. |
| `milestone_removed` | no | The milestone when the metric is removed. | | `milestone_removed` | no | The milestone when the metric is removed. |
| `introduced_by_url` | no | The URL to the Merge Request that introduced the metric. | | `introduced_by_url` | no | The URL to the Merge Request that introduced the metric. |
| `extra` | no | `object`: extra information needed to calculate the metric value. | | `options` | no | `object`: options information needed to calculate the metric value. |
| `skip_validation` | no | This should **not** be set. [Used for imported metrics until we review, update and make them valid](https://gitlab.com/groups/gitlab-org/-/epics/5425). | | `skip_validation` | no | This should **not** be set. [Used for imported metrics until we review, update and make them valid](https://gitlab.com/groups/gitlab-org/-/epics/5425). |
### Metric statuses ### Metric statuses
......
...@@ -26,7 +26,7 @@ A metric definition has the [`instrumentation_class`](metrics_dictionary.md) fie ...@@ -26,7 +26,7 @@ A metric definition has the [`instrumentation_class`](metrics_dictionary.md) fie
The defined instrumentation class should have one of the existing metric classes: `DatabaseMetric`, `RedisHLLMetric`, or `GenericMetric`. The defined instrumentation class should have one of the existing metric classes: `DatabaseMetric`, `RedisHLLMetric`, or `GenericMetric`.
Using the instrumentation classes ensures that metrics can fail safe individually, without breaking the entire Using the instrumentation classes ensures that metrics can fail safe individually, without breaking the entire
process of Usage Ping generation. process of Usage Ping generation.
We have built a domain-specific language (DSL) to define the metrics instrumentation. We have built a domain-specific language (DSL) to define the metrics instrumentation.
...@@ -53,20 +53,17 @@ end ...@@ -53,20 +53,17 @@ end
## Redis HyperLogLog metrics ## Redis HyperLogLog metrics
[Example of a merge request that adds a `RedisHLL` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60089/diffs). [Example of a merge request that adds a `RedisHLL` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61685).
```ruby Count unique values for `i_quickactions_approve` event.
module Gitlab
module Usage ```yaml
module Metrics time_frame: 28d
module Instrumentations data_source: redis_hll
class CountUsersUsingApproveQuickActionMetric < RedisHLLMetric instrumentation_class: 'Gitlab::Usage::Metrics::Instrumentations::RedisHLLMetric'
event_names :i_quickactions_approve options:
end events:
end - i_quickactions_approve
end
end
end
``` ```
## Generic metrics ## Generic metrics
......
...@@ -8,9 +8,11 @@ module Gitlab ...@@ -8,9 +8,11 @@ module Gitlab
include Gitlab::Utils::UsageData include Gitlab::Utils::UsageData
attr_reader :time_frame attr_reader :time_frame
attr_reader :options
def initialize(time_frame:) def initialize(time_frame:, options: {})
@time_frame = time_frame @time_frame = time_frame
@options = options
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class CountUsersUsingApproveQuickActionMetric < RedisHLLMetric
event_names :i_quickactions_approve
end
end
end
end
end
...@@ -7,20 +7,24 @@ module Gitlab ...@@ -7,20 +7,24 @@ module Gitlab
class RedisHLLMetric < BaseMetric class RedisHLLMetric < BaseMetric
# Usage example # Usage example
# #
# class CountUsersVisitingAnalyticsValuestreamMetric < RedisHLLMetric # In metric YAML defintion
# event_names :g_analytics_valuestream # instrumentation_class: RedisHLLMetric
# events:
# - g_analytics_valuestream
# end # end
class << self def initialize(time_frame:, options: {})
def event_names(events = nil) super
@metric_events = events
end raise ArgumentError, "options events are required" unless metric_events.present?
end
attr_reader :metric_events def metric_events
options[:events]
end end
def value def value
redis_usage_data do redis_usage_data do
event_params = time_constraints.merge(event_names: self.class.metric_events) event_params = time_constraints.merge(event_names: metric_events)
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**event_params) Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**event_params)
end end
...@@ -35,7 +39,7 @@ module Gitlab ...@@ -35,7 +39,7 @@ module Gitlab
when '7d' when '7d'
{ start_date: 7.days.ago.to_date, end_date: Date.current } { start_date: 7.days.ago.to_date, end_date: Date.current }
else else
raise "Unknown time frame: #{time_frame} for TimeConstraint" raise "Unknown time frame: #{time_frame} for RedisHLLMetric"
end end
end end
end end
......
...@@ -7,9 +7,12 @@ module Gitlab ...@@ -7,9 +7,12 @@ module Gitlab
def uncached_data def uncached_data
::Gitlab::Usage::MetricDefinition.all.map do |definition| ::Gitlab::Usage::MetricDefinition.all.map do |definition|
instrumentation_class = definition.attributes[:instrumentation_class] instrumentation_class = definition.attributes[:instrumentation_class]
options = definition.attributes[:options]
if instrumentation_class.present? if instrumentation_class.present?
metric_value = "Gitlab::Usage::Metrics::Instrumentations::#{instrumentation_class}".constantize.new(time_frame: definition.attributes[:time_frame]).value metric_value = "Gitlab::Usage::Metrics::Instrumentations::#{instrumentation_class}".constantize.new(
time_frame: definition.attributes[:time_frame],
options: options).value
metric_payload(definition.key_path, metric_value) metric_payload(definition.key_path, metric_value)
else else
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountUsersUsingApproveQuickActionMetric, :clean_gitlab_redis_shared_state do RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisHLLMetric, :clean_gitlab_redis_shared_state do
before do before do
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 1, time: 1.week.ago) Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 1, time: 1.week.ago)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 1, time: 2.weeks.ago) Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 1, time: 2.weeks.ago)
...@@ -10,6 +10,10 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountUsersUsingApproveQ ...@@ -10,6 +10,10 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountUsersUsingApproveQ
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 2, time: 2.months.ago) Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 2, time: 2.months.ago)
end end
it_behaves_like 'a correct instrumented metric value', { time_frame: '28d', data_source: 'redis_hll' }, 2 it_behaves_like 'a correct instrumented metric value', { time_frame: '28d', options: { events: ['i_quickactions_approve'] } }, 2
it_behaves_like 'a correct instrumented metric value', { time_frame: '7d', data_source: 'redis_hll' }, 1 it_behaves_like 'a correct instrumented metric value', { time_frame: '7d', options: { events: ['i_quickactions_approve'] } }, 1
it 'raise exception if vents options is not present' do
expect { described_class.new(time_frame: '28d') }.to raise_error(ArgumentError)
end
end end
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'a correct instrumented metric value' do |options, expected_value| RSpec.shared_examples 'a correct instrumented metric value' do |params, expected_value|
let(:time_frame) { options[:time_frame] } let(:time_frame) { params[:time_frame] }
let(:options) { params[:options] }
before do before do
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false) allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
end end
it 'has correct value' do it 'has correct value' do
expect(described_class.new(time_frame: time_frame).value).to eq(expected_value) expect(described_class.new(time_frame: time_frame, options: options).value).to eq(expected_value)
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment