GitLab開発における機能フラグ

note
このドキュメントでは、GitLab製品の開発に貢献する方法を説明します。機能フラグを使って自分のアプリケーションの機能を表示したり非表示にしたりしたい場合は、代わりにこの機能フラグ情報をご覧ください。
caution
新しく導入された機能フラグは、デフォルトではすべて無効になっているはずです。

この文書は、機能フラグの内部使用を改善するためのエピックとして、継続的な作業の対象です。提案があれば、新しいイシューとして提起し、エピックに添付してください。

機能フラグのライフサイクルの概要や機能フラグを使うべきかどうかの判断にお困りの場合は、機能フラグのライフサイクルハンドブックのページをご覧ください。

機能フラグを使用するタイミング

ハンドブックの「機能フラグを使用する場合」に移動しました。

GitLab 開発における機能フラグ

機能フラグを活用するかどうかを決める際には、以下の点を考慮する必要があります:

  • 機能フラグはデフォルトで無効でなければなりません。
  • 機能フラグのアカウンティングの必要性を減らすために、機能フラグはできるだけ短い期間コードベースに残すべきです。
  • 機能フラグをオペレーションする人は、機能フラグの背後にある機能のステータスを、ドキュメントや他の利害関係者に明確に伝える責任があります。機能フラグが必要であることが明らかになり次第、イシューの説明を機能フラグ名とデフォルトのオン/オフで更新する必要があります。
  • 機能フラグを導入したり、その状態を更新したり、機能が安定したと判断して既存の機能フラグを削除したりするマージリクエストには、~"機能フラグ "ラベルを付ける必要があります。

機能の実装が複数のマージリクエストにまたがって提供される場合:

  1. フラグを使用する最初のマージリクエストで、デフォルトでオフになる新しい機能フラグを作成してください。フラグを個別に追加すべきではありません
  2. 機能フラグがオンの場合にのみ新しいコードが追加されるように、1つ以上のマージリクエストでインクリメンタルな変更を提出してください。開発者のローカルGDKでは、機能フラグを有効にしておくことができます。
  3. 機能が他のチームメンバーによってテストされる準備ができたら、最初のドキュメントを作成します。機能フラグの状態についての詳細を含めてください。
  4. 特定のプロジェクトで機能フラグを有効にし、実装にイシューがないことを確認してください。gitlab のような公開プロジェクトでは、ドキュメントがない場合は機能フラグを有効にしないでください。チームメンバーや貢献者は、公開プロジェクトで機能フラグが有効になっているのを見たら、その機能の使い方のドキュメントを探すかもしれません。
  5. その機能を本番環境で使用できるようになったら、マージリクエストを次の宛先に送ってください:
    • 最新のフラグステータスを説明するためにドキュメントを更新してください。
    • 変更履歴を追加しました。
    • 機能フラグをデフォルトでオンにするか、完全に削除して新しい動作を有効にします。

機能フラグがあると、機能のリリースが少なくとも1ヶ月(=1リリース)遅れると考えたくなるかもしれません。そうではありません。機能フラグは特定の期間 (たとえば最低でも 1 リリース) 存在する必要はありません。安定とは、GitLab.com上で機能停止などの問題を起こすことなく動作することを意味します。

メインブランチが壊れるリスク

機能フラグは、それを導入した MR で使用しなければなりません。そうしないと、rspec:feature-flags ジョブがmain ブランチでのみ実行されるため、メインブランチが壊れるというシナリオが発生します。

機能フラグの種類

想定される使用方法に合った機能フラグの種類を選択してください。

development タイプ

development 機能フラグは短命の機能フラグで、未完成のコードを本番環境にデプロイするときに使います。GitLab で使われる機能フラグのほとんどはdevelopment 型です。

development 機能フラグには、機能フラグロールアウトテンプレートから作成されたロールアウトイシューが必要です。

development 機能フラグのフォーマットはFeature.<state>(:<dev_flag_name>) です。これらを有効/無効にするには、GitLab Railsコンソールで実行します:

# To enable it for the instance:
Feature.enable(:<dev_flag_name>)

# To disable it for the instance:
Feature.disable(:<dev_flag_name>)

# To enable for a specific project:
Feature.enable(:<dev_flag_name>, Project.find(<project id>))

# To disable for a specific project:
Feature.disable(:<dev_flag_name>, Project.find(<project id>))

development 機能フラグの状態を確認するには:

# Check if the feature flag is enabled
Feature.enabled?(:dev_flag_name)

# Check if the feature flag is disabled
Feature.disabled?(:dev_flag_name)

development 機能フラグの場合、タイプを指定する必要はありません(デフォルトのタイプです)。

ops タイプ

ops 機能フラグは、GitLab製品の動作のオペレーション面を制御する長期間の機能フラグです。例えば、Sidekiqワーカーの動作など、パフォーマンスに影響を与える可能性のある機能を無効にする機能フラグです。

ops 機能フラグは、いつ有効になったり無効になったりするのかを予測するのが難しいので、ロールアウトの問題はないでしょう。

ops 機能フラグを呼び出すには、type: :ops

# Check if feature flag is enabled
Feature.enabled?(:my_ops_flag, project, type: :ops)

# Check if feature flag is disabled
Feature.disabled?(:my_ops_flag, project, type: :ops)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_ops_flag, project, type: :ops)

experiment タイプ

experiment 機能フラグはGitLab.comのA/Bテストに使用されます。

experiment 機能フラグは、development 機能フラグと同じ標準に準拠しなければなりませんが、インターフェイスに若干の違いがあります。実験機能フラグには、実験トラッキングテンプレートを使って作成したロールアウトイシューが必要です。詳細は実験ガイドをご覧ください。

worker タイプ

worker 機能フラグは、Sidekiqジョブの延期など、Sidekiqワーカーの動作を制御するために使用されます。

worker run_sidekiq_jobs_AuthorizedProjectsWorker 機能フラグは、ワーカー名そのものを使って動的に生成されるため、YAML定義はありません。worker タイプの機能フラグを使用するいくつかの例が、Sidekiqジョブの延期にあります。

機能フラグの定義と検証

GitLab 13.3 で導入されました

開発中(RAILS_ENV=development)またはテスト中(RAILS_ENV=test)、すべての機能フラグの使用は厳密に検証されます。

このプロセスは、コードベースにおける機能フラグの一貫した使用を保証するためのものです。すべての機能フラグは、次のようにしなければなりません:

  • 既知であること。明示的に定義された機能フラグのみを使用すること。
  • 二重に定義しないこと。FOSSかEEのどちらかで定義されなければなりませんが、両方で定義することはできません。
  • 全ての呼び出しにおいて有効で一貫性のあるtype: を使用してください。
  • オーナーを持つこと。

GitLab が知っている機能フラグはすべて、YAML ファイルに格納されています:

それぞれの機能フラグは、いくつかのフィールドからなる個別の YAML ファイルで定義されています:

項目必須説明
nameyes機能フラグの名前。
typeyes機能フラグの種類。
default_enabledyes機能フラグのデフォルト状態。
introduced_by_urlいいえ機能フラグを導入したマージリクエストのURL。
rollout_issue_urlいいえ機能フラグのロールアウトに関するイシューのURL。
milestoneいいえ機能フラグが作成されたマイルストーン。
groupいいえ機能フラグを所有するグループ
note
RAILS_ENV=production で実行すると、すべてのバリデーションがスキップされます。

新しい機能フラグの作成

note
GitLab Pagesでは、機能フラグを作成する際に異なるプロセスを用います。

GitLabコードベースは、新しい機能フラグ定義を作成するための専用ツールbin/feature-flag 。このツールは新しい機能フラグについて様々な質問をし、config/feature_flags もしくはee/config/feature_flagsにYAML定義を作成します。

YAML 定義ファイルを持つ機能フラグのみが、開発環境やテスト環境で使用できます。

$ bin/feature-flag my_feature_flag
>> Specify the group introducing the feature flag, like `group::project management`:
?> group::application performance

>> URL of the MR introducing the feature flag (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602

>> Open this URL and fill in the rest of the details:
https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue%5Btitle%5D=%5BFeature+flag%5D+Rollout+of+%60test-flag%60&issuable_template=Feature+Flag+Roll+Out

>> URL of the rollout issue (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/232533
create config/feature_flags/development/my_feature_flag.yml
---
name: my_feature_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232533
group: group::application performance
type: development
default_enabled: false

新しく導入された機能フラグはすべて デフォルトでは無効です.

機能フラグの後ろで開発されマージされた機能は、変更ログエントリを含めるべきではありません。エントリは機能フラグを削除するマージリクエストか、機能フラグのデフォルト値が有効に設定されているマージリクエストのどちらかに追加されるべきです。その機能にデータベースのマイグレーションが含まれている場合、データベースの変更に関する変更ログエントリを含めるべきです

note
EE でのみ使用される機能フラグを作成するには、--ee フラグを追加します:bin/feature-flag --ee

新しいフラグの命名

新しい機能フラグの名前を決めるときは、以下のガイドラインを考慮してください:

  • 短くてわかりにくい名前よりも、長くて説明的な名前のほうがよいでしょう。
  • 名前はスネークケース(my_cool_feature_flag)で書きましょう。
  • 二重否定で考える(文書化する)ことを避けるため、disable を名前に使うことは避けてください。hide_,remove_,disallow_で名前を始めることを検討してください。

    ソフトウェア工学では、この問題は「ブーリアン変数の否定名」として知られています。しかし、フラグをデフォルトで無効にしたり、フラグの後ろに移動して機能フラグを削除したり、アクターによってフラグを選択的に無効にしたりするために、否定語を完全に禁止することはできません。

マスター(メイン)ブランチが壊れるリスク

caution
機能フラグは、それを導入した MR で使用しなければなりません。そうしないと、rspec:feature-flags ジョブがmaster ブランチでのみ実行されるため、壊れた masterシナリオが発生します。

すべての機能フラグのリスト

ChatOpsを使用して環境内のすべての機能フラグをSlackに出力するには、run feature list コマンドを使用します。例えば

/chatops run feature list --dev
/chatops run feature list --staging

機能フラグの切り替え

機能フラグのトグルについての詳細は、変更のロールアウトをご覧ください。

機能フラグの削除

機能フラグの削除については、機能フラグのクリーンアップを参照してください。

機能フラグを使った開発者

GitLabコードベースで機能フラグを使うには、主に2つの方法があります:

バックエンド

機能フラグインタフェースはlib/feature.rbで定義されています。このインターフェイスは、機能フラグが有効か無効かをチェックするためのメソッド群を提供します:

if Feature.enabled?(:my_feature_flag, project)
  # execute code if feature flag is enabled
else
  # execute code if feature flag is disabled
end

if Feature.disabled?(:my_feature_flag, project)
  # execute code if feature flag is disabled
end

設定されていない機能フラグのデフォルトのふるまいは YAML 定義のdefault_enabled: によって制御されます。

機能フラグに YAML 定義がない場合、開発環境やテスト環境ではエラーが発生し、運用環境ではfalse が返されます。

指定しない場合、Feature.enabled?Feature.disabled? のデフォルトの機能フラグタイプはtype: development です。その他の機能フラグタイプについては、type: を指定する必要があります:

if Feature.enabled?(:feature_flag, project, type: :ops)
  # execute code if ops feature flag is enabled
else
  # execute code if ops feature flag is disabled
end

if Feature.disabled?(:my_feature_flag, project, type: :ops)
  # execute code if feature flag is disabled
end
caution
アプリケーションのロード時に機能フラグを使用しないでください。例えば、config/initializers/* またはクラス・レベルでFeature クラスを使用すると、予期しないエ ラーが発生することがあります。このエラーは、機能フラグ・アダプタが依存する可能性のあるデータベースがロード時に存在しないために発生します (特に、新規インストールの場合)。データベースをまったく必要としないアダプタもあります (たとえば HTTP アダプタなど)。機能フラグの設定チェックは、Feature 名前空間で抽象化する必要があります。この方法では、機能フラグが変更されたときにアプリケーションをリロードする必要があります。そのため、本番環境で Web/API/Sidekiq フリートをリロードするよう SRE に依頼する必要があり、変更を完全にロールアウト/ロールバックするには時間がかかります。このような理由から、代わりに環境変数(たとえば、ENV['YOUR_FEATURE_NAME'] )またはgitlab.yml を使用します。

避けるべきパターンの例を示します:

class MyClass
  if Feature.enabled?(:...)
    new_process
  else
    legacy_process
  end
end

再帰の検出

機能フラグがたくさんある場合、どこで呼び出されるかは必ずしも明らかではありません。ある機能フラグの評価が他の機能フラグの評価を必要とするようなサイクルは避けてください。サイクルが発生すると、そのサイクルは解除され、デフォルト値が返されます。

この再帰検出が正しく機能するように、機能値へのアクセスは常にFeature::enabled? を通して行い、Feature::get を低レベルで使用することは避けてください。このような現象が発生した場合、Feature::RecursionError 例外がエラートラッカーに追跡されます。

フロントエンド

UI要素に機能フラグを使用する場合、もしあれば、バックエンドの_コードにも_機能フラグを使用してください。こうすることで、その機能が有効になるまで、絶対にその機能を使うことができないようになります。

ApplicationController を継承するすべてのコントローラで使用できるpush_frontend_feature_flag メソッドを使用してください。 このメソッドを使用して、たとえば機能フラグの状態を公開することができます:

before_action do
  # Prefer to scope it per project or user, for example
  push_frontend_feature_flag(:vim_bindings, project)
end

def index
  # ...
end

def edit
  # ...
end

機能フラグの状態は、JavaScript で次のように確認できます:

if ( gon.features.vimBindings ) {
  // ...
}

JavaScriptの機能フラグの名前は常にキャメルケースなので、gon.features.vim_bindings

Vueコンポーネントで機能フラグにアクセスする方法の詳細については、Vueガイドを参照してください。

指定しない場合、push_frontend_feature_flag のデフォルトの機能フラグタイプはtype: development です。その他の機能フラグタイプについては、type: を指定する必要があります:

before_action do
  push_frontend_feature_flag(:vim_bindings, project, type: :ops)
end

機能アクター

機能フラグにはアクタを使用することを強くお勧めします。アクタは、特定のプロジェクト、グループ、ユーザーに対してのみ機能フラグを有効にする簡単な方法を提供します。これにより、たとえばアクターに基づいてログやエラーをフィルタリングできるため、デバッグが容易になります。また、gitlab-org またはgitlab-com グループで最初に機能を有効にし、他のユーザーには影響を与えないようにすることもできます。

アクターはまた、粘着性のある方法で機能のパーセンテージロールアウトを行う簡単な方法を提供します。1%のロールアウトで特定のアクターが機能を有効にした場合、そのアクターは10%、50%、100%でも機能を有効にし続けることができます。

GitLabは現在、以下のモデルを機能フラグアクターとしてサポートしています:

  • User
  • Project
  • Group

アクタはFeature.enabled? 呼び出しの Feature.enabled?2番目のパラメータです。Feature.enabled?.NETのすべての呼び出しで、同じアクター型を一貫して使用する必要が Feature.enabled?あります。

Feature.enabled?(:feature_flag, project)
Feature.enabled?(:feature_flag, group)
Feature.enabled?(:feature_flag, user)

ステージングやプロダクションのようなGitLabが提供する環境で、ChatOpsを使って機能フラグを選択的に有効/無効にする方法の詳細については、GitLabの開発における機能フラグを参照してください。

本番環境での検証にアクタを使用

caution
本番環境をテスト環境として使用することは推奨されません。本番環境にない機能のテストには、当社のテスト環境を使用してください。

ステージング環境は本番環境に似た環境で機能をテストする方法を提供しますが、本番環境に特有のパフォーマンスメトリクスの前後を比較することはできません。Sitespeedレポートのようなツールで機能フラグの下にある新しいコードのメトリクスを明らかにできるようにするために、開発者の機能フラグが有効になっている本番環境のプロジェクトがあると便利です。

Sitespeedで古いコードベースをすでにトラッキングしている場合、このアプローチはさらに有効で、機能フラグのロールアウト前後のパフォーマンスを正確に比較できます。

アクタとして追加オブジェクトを有効化

アクターに基づくフィーチャー・ゲートを使用するには、モデルがflipper_id に応答する必要があります。たとえば、Foo モデルで有効にするには、次のようにします:

class Foo < ActiveRecord::Base
  include FeatureGate
end

include FeatureGate またはflipper_id メソッドを公開するモデルだけが、Feature.enabled? のアクターとして使用できます。

ライセンスされた機能の機能フラグ

ライセンスされた機能名と同じ名前の機能フラグを使用することはできません。これは広く議論され、混乱を招くため削除されました。

ライセンスされた機能をチェックするには、例えば、別の名前で専用の機能フラグを追加し、明示的にチェックします:

Feature.enabled?(:licensed_feature_feature_flag, project) &&
  project.feature_available?(:licensed_feature)

機能グループ

フィーチャーグループは、lib/feature.rb.register_feature_groups メソッド内)で静的に定義する必要がありますが、その実装は動的にも可能です(DBへのクエリなど)。

lib/feature.rb で定義すると、features API のfeature_group パラメータで、指定したフィーチャーグループのフィーチャーをアクティブにすることができます。

利用可能な機能グループは以下の通りです:

グループ名スコープ対象説明
gitlab_team_membersユーザーのメンバーであるユーザーに対して、この機能を有効にします。gitlab-com

フィーチャーグループはグループ名で有効にできます:

Feature.enable(:feature_flag_name, :gitlab_team_members)

ローカルで機能フラグを有効にする(開発中)

rails コンソール (rails c) で、以下のコマンドを入力して機能フラグを有効にします:

Feature.enable(:feature_flag_name)

同様に、次のコマンドは機能フラグを無効にします:

Feature.disable(:feature_flag_name)

指定したゲートの機能フラグを有効にすることもできます:

Feature.enable(:feature_flag_name, Project.find_by_full_path("root/my-project"))

ローカルで機能フラグを無効にする(開発中)

Railsコンソールから手動で機能フラグを有効/無効にすると、デフォルト値が上書きされます。このため、フラグのdefault_enabled 属性を変更する際に混乱を招く可能性があります。

機能フラグをデフォルトの状態に戻すには、railsコンソール(rails c)で以下のようにして無効にします:

Feature.remove(:feature_flag_name)

チェンジログ

エンドユーザーが直接アクセスできない機能(例:その機能を使用する機能)、または間接的にアクセスできない機能(例:バックグラウンドジョブを利用する機能、パフォーマンスの向上、データベースのマイグレーション更新)については、変更履歴の紹介を避けたいと考えています。

  • データベースのマイグレーションは、セルフマネージドユーザーがアップグレード前にデータベースの変更を認識する必要があるため、常に間接的にエンドユーザーがアクセスできます。このため、変更ログエントリが必要です。
  • デフォルトで無効になっている機能フラグの背後にある変更は、変更ログエントリを持つべきではありません
  • デフォルトで有効になっている機能フラグの背後にある変更は、変更ログエントリを持つべきです
  • 機能フラグ自体の変更 (フラグの削除、デフォルトオンの設定) は、変更ログエントリを 持つべきです。フローチャートを使って、変更ログエントリーのタイプを決定してください。

    graph LR A[flag: default off] -->|'added' / 'changed' / 'fixed' / '...'| B(flag: default on) B -->|'other'| C(remove flag, keep new code) B -->|'removed' / 'changed'| D(remove flag, keep old code) A -->|'added' / 'changed' / 'fixed' / '...'| C A -->|no changelog| D
  • 機能フラグの変更ログは、機能フラグではなく、その機能について記述すべきです。ただし、機能フラグのデフォルトオンが新しいコードで削除された場合は除きます ( 上のフローチャートではother )。
  • 機能フラグはバグフィックスやメンテナンス作業のロールアウトにも使うことができます。このシナリオでは、変更履歴はそれに関連したものでなければなりません。たとえば、fixedother などです。

テストの機能フラグ

機能フラグをコードベースに導入すると、テストすべきコードパスが追加されます。機能フラグの影響を受けるすべてのコードに対して、その機能が正しく動作することを確認するために、有効時と無効時の両方の自動テストを含めることを強く推奨します。自動テストが両方の状態に含まれていない場合は、テストされていないコードパスに関連する機能を、本番環境にデプロイする前に手動でテストする必要があります。

テスト環境を使用する場合、すべての機能フラグはデフォルトで有効になっています。spec/spec_helper.rb ファイル で、フラグをデフォルトで無効にすることができます。フラグを無効にする必要がある理由をインラインでコメントしてください。可能であれば、参照用にイシューの URL も添付してください。

caution
これはエンド・ツー・エンド・テスト((QA) )には適用されません。エンド・ツー・エンド・テストはデフォルトでは機能フラグを有効にしません。エンドツーエンドテストで機能フラグを使用するには、別のプロセスがあります。

テストの機能フラグを無効にするには、stub_feature_flags ヘルパーを使用します。たとえば、ci_live_trace 機能フラグをテスト内でグローバルに無効にするには、次のようにします:

stub_feature_flags(ci_live_trace: false)

Feature.enabled?(:ci_live_trace) # => false

両方のパスをテストする一般的なパターンは、次のようになります:

it 'ci_live_trace works' do
  # tests assuming ci_live_trace is enabled in tests by default
  Feature.enabled?(:ci_live_trace) # => true 
end

context 'when ci_live_trace is disabled' do
  before do
    stub_feature_flags(ci_live_trace: false)
  end

  it 'ci_live_trace does not work' do
    Feature.enabled?(:ci_live_trace) # => false
  end
end

機能フラグがあるアクターでのみ有効で、他のアクターでは有効でないテストを設定したい場合、ヘルパーに渡すオプションでこれを指定できます。たとえば、特定のプロジェクトでci_live_trace 機能フラグを有効にする場合:

project1, project2 = build_list(:project, 2)

# Feature will only be enabled for project1
stub_feature_flags(ci_live_trace: project1)

Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, project1) # => true
Feature.enabled?(:ci_live_trace, project2) # => false

FlipperGate の動作は次のようになります:

  1. 指定したアクタのオーバーライドを有効にすることができます。
  2. 指定したアクタのオーバーライドを無効(削除)にして、デフォルトの状態に戻すことができます。
  3. 指定したアクタを明示的に無効にしたことをモデル化する方法はありません。
Feature.enable(:my_feature)
Feature.disable(:my_feature, project1)
Feature.enabled?(:my_feature) # => true
Feature.enabled?(:my_feature, project1) # => true

Feature.disable(:my_feature2)
Feature.enable(:my_feature2, project1)
Feature.enabled?(:my_feature2) # => false
Feature.enabled?(:my_feature2, project1) # => true

have_pushed_frontend_feature_flags

have_pushed_frontend_feature_flags を使用して、push_frontend_feature_flag が機能フラグを HTML に追加したかどうかをテストします。

例えば、

stub_feature_flags(value_stream_analytics_path_navigation: false)

visit group_analytics_cycle_analytics_path(group)

expect(page).to have_pushed_frontend_feature_flags(valueStreamAnalyticsPathNavigation: false)

stub_feature_flagsFeature.enable*

テスト環境で機能フラグを有効にするには、stub_feature_flags 。この方法は、単純なユースケースに対して、シンプルでよく記述されたインターフェイスを提供します。

しかし、場合によっては、機能フラグのパーセンテージロールアウトのように、より複雑な動作をテストする必要があります。これには.enable_percentage_of_time または.enable_percentage_of_actors を使用します:

# Good: feature needs to be explicitly disabled, as it is enabled by default if not defined
stub_feature_flags(my_feature: false)
stub_feature_flags(my_feature: true)
stub_feature_flags(my_feature: project)
stub_feature_flags(my_feature: [project, project2])

# Bad
Feature.enable(:my_feature_2)

# Good: enable my_feature for 50% of time
Feature.enable_percentage_of_time(:my_feature_3, 50)

# Good: enable my_feature for 50% of actors/gates/things
Feature.enable_percentage_of_actors(:my_feature_4, 50)

定義された状態を持つ各機能フラグは、テスト実行時に永続化されます:

Feature.persisted_names.include?('my_feature') => true
Feature.persisted_names.include?('my_feature_2') => true
Feature.persisted_names.include?('my_feature_3') => true
Feature.persisted_names.include?('my_feature_4') => true

スタビング・アクタ

特定のアクタに対してのみ機能フラグを有効にしたい場合、そのアクタの表現をスタブすることができます。Feature.enabled?Feature.disabled? に引数として渡されるゲートは、FeatureGate を含むオブジェクトでなければなりません。

仕様では、カスタム・アクタを素早く作成できるstub_feature_flag_gate メソッドを使用できます:

gate = stub_feature_flag_gate('CustomActor')

stub_feature_flags(ci_live_trace: gate)

Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, gate) # => true

また、特定のアクタの機能フラグを無効にすることもできます:

gate = stub_feature_flag_gate('CustomActor')

stub_feature_flags(ci_live_trace: false, thing: gate)

テストでの機能フラグエンジンの制御

テスト環境のフリッパーエンジンは、メモリモードFlipper::Adapters::Memory で動作します。productiondevelopment モードでは、Flipper::Adapters::ActiveRecordを使用します。

Flipper::Adapters::Memory モードとActiveRecord モードのどちらを使用するかを制御することができます。

stub_feature_flags: true (デフォルトと優先)

このモードでは、Flipper はFlipper::Adapters::Memory を使用するように設定され、すべての機能フラグをオンバイデフォルトとし、初回使用時に永続化します。

機能フラグの動作が、特定のコンテキスト以外ではテストされないようにします。

stub_feature_flags: false

これは、メモリスタブのフリッパーを無効にし、productiondevelopment で使用されるモードFlipper::Adapters::ActiveRecord を使用します。

このモードは、フリッパーとActiveRecord との相互作用をテストしたい場合にのみ使用してください。

エンドツーエンド(QA) テスト

機能フラグの切り替えは、エンドツーエンド(QA) テストでは動作が異なります。エンドツーエンドのテストフレームワークは Rails やデータベースに直接アクセスできないため、Flipper を使うことができません。代わりに公開 API を使います。各エンドツーエンドテストでは、テスト中に機能フラグを有効/無効にできます。あるいは、GitLab リポジトリのqa ディレクトリからテストを実行するとき、あるいは](https://gitlab.com/gitlab-org/gitlab/-/tree/master/qa#running-tests-with-a-feature-flag-enabled-or-disabled)GitLab QA 経由でテストを実行](https://gitlab.com/gitlab-org/gitlab/-/tree/master/qa#running-tests-with-a-feature-flag-enabled-or-disabled)するときに、1 つ以上のテストの前に機能フラグを有効/無効にすることもできます。

前述のとおり、機能フラグはエンドツーエンドテストではデフォルトでは有効になりません。つまり、機能フラグを明示的に有効/無効にするようにテストが書かれていない限り、エンドツーエンドのテストは機能フラグがソースコードに実装されているデフォルトの状態、あるいはテスト対象の GitLab インスタンスで機能フラグが現在の状態になっている状態で実行されるということです。

機能フラグがステージやGitLab.com上で変更されると、パイプライントリアージDRIに通知するためにSlackメッセージが#qa-staging#qa-production チャンネルに投稿されます。しかし、もしあなたが変更に取り組んでいるのであれば、機能フラグを有効にした状態でエンドツーエンドのテストがパスすることを確認することで、予期せぬ失敗を避けることができます。

機能フラグによるSidekiqワーカーの動作制御

worker タイプ の機能フラグを使用して、Sidekiq ワーカーの動作を制御することができます。

Sidekiqジョブの延期

無効にすると、run_sidekiq_jobs_{WorkerName} の形式の機能フラグは、ジョブを後でスケジューリングすることにより、ワーカーの実行を遅らせます。この機能フラグは、デフォルトではすべてのワーカーで有効になっています。ジョブを遅延させることは、ワーカーインスタンスによる争いの多い動作がインフラリソース (データベースやデータベース接続プールなど) を飽和させているような場合に有効です。実装はSkipJobs Sidekiq サーバミドルウェアにあります。

note
機能フラグが無効である限り、ジョブは無期限に延期されます。ワーカーが処理を続行しても安全であると判断された後、機能フラグを削除することが重要です。

false に設定すると、100% のジョブが遅延されます。処理を再開させたいときは、時間ロールアウトのパーセンテージを使うことができます。例えば

# not running any jobs, deferring all 100% of the jobs
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker false

# only running 10% of the jobs, deferring 90% of the jobs
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker 10

# running 50% of the jobs, deferring 50% of the jobs
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker 50

# back to running all jobs normally
/chatops run feature delete run_sidekiq_jobs_SlowRunningWorker

Sidekiqジョブの削除

ジョブを延期する代わりに、機能フラグdrop_sidekiq_jobs_{WorkerName} を有効にすることで、ジョブを完全に削除することができます。この機能フラグを使用するのは、ジョブを削除しても安全であることが確実な場合、つまりジョブを将来処理する必要がない場合です。

# drop all the jobs
/chatops run feature set drop_sidekiq_jobs_SlowRunningWorker true

# process jobs normally
/chatops run feature delete drop_sidekiq_jobs_SlowRunningWorker
note
drop_sidekiq_jobs_{WorkerName}run_sidekiq_jobs_{WorkerName}つまり、drop_sidekiq_jobs が有効で、drop_sidekiq_jobs_{WorkerName} が無効の場合、ジョブは完全に削除されます。