Welcome to mirror list, hosted at ThFree Co, Russian Federation.

application_experiment.rb « experiments « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 019e74bcd480a56ccb2ab42fccb62fddd1d0c133 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# frozen_string_literal: true

class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/NamespacedClass
  def enabled?
    return false if Feature::Definition.get(feature_flag_name).nil? # there has to be a feature flag yaml file
    return false unless Gitlab.dev_env_or_com? # we have to be in an environment that allows experiments

    # the feature flag has to be rolled out
    Feature.get(feature_flag_name).state != :off # rubocop:disable Gitlab/AvoidFeatureGet
  end

  def publish(_result = nil)
    track(:assignment) # track that we've assigned a variant for this context

    # push the experiment data to the client
    begin
      Gon.push({ experiment: { name => signature } }, true) # push the experiment data to the client
    rescue NoMethodError
      # means we're not in the request cycle, and can't add to Gon. Log a warning maybe?
    end
  end

  def track(action, **event_args)
    return unless should_track? # don't track events for excluded contexts

    # track the event, and mix in the experiment signature data
    Gitlab::Tracking.event(name, action.to_s, **event_args.merge(
      context: (event_args[:context] || []) << SnowplowTracker::SelfDescribingJson.new(
        'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0', signature
      )
    ))
  end

  def exclude!
    @excluded = true
  end

  def rollout_strategy
    # no-op override in inherited class as desired
  end

  def variants
    # override as desired in inherited class with all variants + control
    # %i[variant1 variant2 control]
    #
    # this will make sure we supply variants as these go together - rollout_strategy of :round_robin must have variants
    raise NotImplementedError, "Inheriting class must supply variants as an array if :round_robin strategy is used" if rollout_strategy == :round_robin
  end

  private

  def feature_flag_name
    name.tr('/', '_')
  end

  def resolve_variant_name
    case rollout_strategy
    when :round_robin
      round_robin_rollout
    else
      percentage_rollout
    end
  end

  def round_robin_rollout
    Strategy::RoundRobin.new(feature_flag_name, variants).execute
  end

  def percentage_rollout
    return variant_names.first if Feature.enabled?(feature_flag_name, self, type: :experiment, default_enabled: :yaml)

    nil # Returning nil vs. :control is important for not caching and rollouts.
  end
end