バリューストリームアナリティクス開発者ガイドライン

GitLabでバリューストリームアナリティクス(VSA) を設定する方法については、アナリティクスのドキュメントをご覧ください。

バリューストリームアナリティクスはどのように機能するのですか?

Value Stream Analyticsは、2つのタイムスタンプ列またはタイムスタンプ式の間の期間を計算し、データに対して様々な集計を実行します。

使用例:

  • マージリクエスト作成時間とマージリクエストのマージ時間の間の期間。
  • イシュー作成時刻からイシュー終了時刻までの期間

この期間はさまざまな方法で表示されます:

  • 集計:中央値、平均値
  • リスト:個々のマージ リクエストおよびイシュー レコードの期間をリストします。

期間とは別に、ステージ内のレコード数を公開します。

機能の可用性

  • グループレベル(ライセンス):UltimateまたはPremiumのサブスクリプションが必要です。このバージョンは最も機能が充実しています。
  • プロジェクト・レベル(ライセンス):プロジェクト・レベルの VSA に継続的に機能を追加し、グループ・レベルの VSA と同等にします。
  • プロジェクトレベル(FOSS): そのままで結構です。
機能グループレベル (ライセンス)プロジェクトレベル (ライセンス)プロジェクトレベル(FOSS)---  カスタムバリューストリームの作成YesNo, デフォルトステージには1つのバリューストリーム (デフォルト) しか存在しませんNo, デフォルトステージには1つのバリューストリーム (デフォルト) しか存在しません カスタムステージの作成YesNo フィルタリング (作成者、ラベル、マイルストーンなど。ステージタイムチャートYesNoNo合計時間チャートYesNoNoタイプ別タスクチャートYesNoNo|DORAメトリクスYesYesNo|サイクルタイムとリードタイムのサマリー(ライフサイクルメトリクス)YesYesNo|新しいイシュー、コミット、デプロイ (ライフサイクル・メトリクス)Yes(コミットを除く)YesYesNo|集計されたバックエンドを使用YesNo|日付フィルターの動作 日付の範囲内で終了したアイテムをフィルター[](https://gitlab.com/groups/gitlab-org/-/epics/6046)作成日でアイテムをフィルター作成日作成者少なくともレポーター公開可能。

VSAコア・ドメイン・オブジェクト

ステージ

ステージは、イベントペア(開始イベントと終了イベント)にステージ名などのメタデータを追加したものです。ステージはバックエンドで定義されたペアリングルール内でユーザーが設定可能です。

ステージの例コードレビュー

  • 開始イベント識別子:マージリクエスト作成時間。
  • 開始イベント列:merge_requests.created_at タイムスタンプ列を使用します。
  • 終了イベント識別子:マージリクエストのマージ時間。
  • 終了イベント列:merge_request_metrics.merged_at タイムスタンプ列を使用します。
  • ステージイベントハッシュID:開始イベント識別子と終了イベント識別子のペアの計算ハッシュ。
    • 2つのステージが同じ開始イベントと終了イベントの設定を持っている場合、それらのステージイベントハッシュ。IDは同じです。
    • ステージ・イベント・ハッシュIDは、後でパーティショニングされたデータベース・テーブルに集約されたデータを格納するために使用されます。

歴史的に、価値ストリーム分析では、サブスクリプションに関係なくエンドユーザーが常に利用できる6つのステージが定義されています。

値のストリーム

バリューストリームは、ステージのコンテナオブジェクトです。グループごとに、DevOpsライフサイクルのさまざまな側面に焦点を当てた複数のバリューストリームを設定できます。

イベント

イベントは、バリューストリーム分析機能の最小構成要素です。ステージは2つのイベントで構成されます:

  • 開始イベント
  • 終了イベント

これらのイベントは持続時間の計算において重要なロールを果たします。

計算式duration = end_event_time - start_event_time

継続時間の計算を柔軟にするために、Event はそれぞれ別のクラスとして実装されています。これらは計算クエリで使われるタイムスタンプ式を定義する役割を担います。

Event クラスの実装

StageEvent の基底クラスで説明されているように、いくつかのメソッドを実装する必要があります。最も重要なメソッドは次のとおりです:

  • object_type
  • timestamp_projection

object_type メソッドは、計算のためにどのドメイン・オブジェクトをクエリするかを定義します。現在、2つのモデルが使用できます:

  • Issue
  • MergeRequest

持続時間の計算にはtimestamp_projection メソッドが使用されます。

def timestamp_projection
  # your timestamp expression comes here
end

# event will use the issue creation time in the duration calculation
def timestamp_projection
  Issue.arel_table[:created_at]
end

より複雑な式も可能です (例えば、COALESCE を使用します)。例については、既存のイベントクラスをレビューしてください。

場合によっては、timestamp_projection メソッドを定義するだけでは十分ではありません。計算クエリは、どのテーブルにタイムスタンプ式が含まれているかを知っている必要があります。各Event クラスは、timestamp_projection を動作させるために計算クエリに変更を加える責任を負います。これは通常、追加のテーブルに参加することを意味します。

issue_metrics テーブルを結合し、first_mentioned_in_commit_at カラムをタイムスタンプ式として使用する例:

def object_type
  Issue
end

def timestamp_projection
  IssueMetrics.arel_table[:first_mentioned_in_commit_at]
end

def apply_query_customization(query)
  # in this case the query attribute will be based on the Issue model: `Issue.where(...)`
  query.joins(:metrics)
end

開始イベントと終了イベントの検証

いくつかの開始/終了イベントのペアは、互いに「互換性」がありません。例えば

  • 例えば、「イシューの作成」と「マージリクエストの作成」です:イベントクラスは異なるドメインモデルで定義され、object_type メソッドも異なります。
  • 「イシューのクローズ」から「イシューの作成」:イシューをクローズする前に、まずイシューを作成する必要があります。
  • 「イシュー終了」→「イシュー終了期間は常に0です。

StageEvents モジュールには、許可されるstart_eventend_event の組み合わせが記述されています (PAIRING_RULES 定数)。新しいイベントが追加された場合、このモジュールに登録する必要があります。新しいイベントを追加するには

  1. ENUM_MAPPING Stage モデルではenum として使用されます。
  2. PAIRING_RULES ハッシュ内のイベントと互換性のあるイベントを定義します。

サポートされる開始/終了イベントの組み合わせ:

graph LR; IssueCreated --> IssueClosed; IssueCreated --> IssueFirstAddedToBoard; IssueCreated --> IssueFirstAssociatedWithMilestone; IssueCreated --> IssueFirstMentionedInCommit; IssueCreated --> IssueLastEdited; IssueCreated --> IssueLabelAdded; IssueCreated --> IssueLabelRemoved; IssueCreated --> IssueFirstAssignedAt; MergeRequestCreated --> MergeRequestMerged; MergeRequestCreated --> MergeRequestClosed; MergeRequestCreated --> MergeRequestFirstDeployedToProduction; MergeRequestCreated --> MergeRequestLastBuildStarted; MergeRequestCreated --> MergeRequestLastBuildFinished; MergeRequestCreated --> MergeRequestLastEdited; MergeRequestCreated --> MergeRequestLabelAdded; MergeRequestCreated --> MergeRequestLabelRemoved; MergeRequestCreated --> MergeRequestFirstAssignedAt; MergeRequestFirstAssignedAt --> MergeRequestClosed; MergeRequestFirstAssignedAt --> MergeRequestLastBuildStarted; MergeRequestFirstAssignedAt --> MergeRequestLastEdited; MergeRequestFirstAssignedAt --> MergeRequestMerged; MergeRequestFirstAssignedAt --> MergeRequestLabelAdded; MergeRequestFirstAssignedAt --> MergeRequestLabelRemoved; MergeRequestLastBuildStarted --> MergeRequestLastBuildFinished; MergeRequestLastBuildStarted --> MergeRequestClosed; MergeRequestLastBuildStarted --> MergeRequestFirstDeployedToProduction; MergeRequestLastBuildStarted --> MergeRequestLastEdited; MergeRequestLastBuildStarted --> MergeRequestMerged; MergeRequestLastBuildStarted --> MergeRequestLabelAdded; MergeRequestLastBuildStarted --> MergeRequestLabelRemoved; MergeRequestMerged --> MergeRequestFirstDeployedToProduction; MergeRequestMerged --> MergeRequestClosed; MergeRequestMerged --> MergeRequestFirstDeployedToProduction; MergeRequestMerged --> MergeRequestLastEdited; MergeRequestMerged --> MergeRequestLabelAdded; MergeRequestMerged --> MergeRequestLabelRemoved; IssueLabelAdded --> IssueLabelAdded; IssueLabelAdded --> IssueLabelRemoved; IssueLabelAdded --> IssueClosed; IssueLabelAdded --> IssueFirstAssignedAt; IssueLabelRemoved --> IssueClosed; IssueLabelRemoved --> IssueFirstAssignedAt; IssueFirstAddedToBoard --> IssueClosed; IssueFirstAddedToBoard --> IssueFirstAssociatedWithMilestone; IssueFirstAddedToBoard --> IssueFirstMentionedInCommit; IssueFirstAddedToBoard --> IssueLastEdited; IssueFirstAddedToBoard --> IssueLabelAdded; IssueFirstAddedToBoard --> IssueLabelRemoved; IssueFirstAddedToBoard --> IssueFirstAssignedAt; IssueFirstAssignedAt --> IssueClosed; IssueFirstAssignedAt --> IssueFirstAddedToBoard; IssueFirstAssignedAt --> IssueFirstAssociatedWithMilestone; IssueFirstAssignedAt --> IssueFirstMentionedInCommit; IssueFirstAssignedAt --> IssueLastEdited; IssueFirstAssignedAt --> IssueLabelAdded; IssueFirstAssignedAt --> IssueLabelRemoved; IssueFirstAssociatedWithMilestone --> IssueClosed; IssueFirstAssociatedWithMilestone --> IssueFirstAddedToBoard; IssueFirstAssociatedWithMilestone --> IssueFirstMentionedInCommit; IssueFirstAssociatedWithMilestone --> IssueLastEdited; IssueFirstAssociatedWithMilestone --> IssueLabelAdded; IssueFirstAssociatedWithMilestone --> IssueLabelRemoved; IssueFirstAssociatedWithMilestone --> IssueFirstAssignedAt; IssueFirstMentionedInCommit --> IssueClosed; IssueFirstMentionedInCommit --> IssueFirstAssociatedWithMilestone; IssueFirstMentionedInCommit --> IssueFirstAddedToBoard; IssueFirstMentionedInCommit --> IssueLastEdited; IssueFirstMentionedInCommit --> IssueLabelAdded; IssueFirstMentionedInCommit --> IssueLabelRemoved; IssueClosed --> IssueLastEdited; IssueClosed --> IssueLabelAdded; IssueClosed --> IssueLabelRemoved; MergeRequestClosed --> MergeRequestFirstDeployedToProduction; MergeRequestClosed --> MergeRequestLastEdited; MergeRequestClosed --> MergeRequestLabelAdded; MergeRequestClosed --> MergeRequestLabelRemoved; MergeRequestFirstDeployedToProduction --> MergeRequestLastEdited; MergeRequestFirstDeployedToProduction --> MergeRequestLabelAdded; MergeRequestFirstDeployedToProduction --> MergeRequestLabelRemoved; MergeRequestLastBuildFinished --> MergeRequestClosed; MergeRequestLastBuildFinished --> MergeRequestFirstDeployedToProduction; MergeRequestLastBuildFinished --> MergeRequestLastEdited; MergeRequestLastBuildFinished --> MergeRequestMerged; MergeRequestLastBuildFinished --> MergeRequestLabelAdded; MergeRequestLastBuildFinished --> MergeRequestLabelRemoved; MergeRequestLabelAdded --> MergeRequestLabelAdded; MergeRequestLabelAdded --> MergeRequestLabelRemoved; MergeRequestLabelAdded --> MergeRequestMerged; MergeRequestLabelAdded --> MergeRequestFirstAssignedAt; MergeRequestLabelRemoved --> MergeRequestLabelAdded; MergeRequestLabelRemoved --> MergeRequestLabelRemoved; MergeRequestLabelRemoved --> MergeRequestFirstAssignedAt;

デフォルトステージ

価値ストリーム分析のオリジナルの実装では、7つのステージが定義されています。これらのステージは各親に対して常に利用可能ですが、これらのステージを変更することはできません。

物事を効率的にし、作成されるレコードの数を減らすために、デフォルトのステージはメモリ内オブジェクトとして表現されます (永続化されません)。ユーザーが初めてカスタム・ステージを作成すると、すべてのステージが永続化されます。この動作は、値のストリーム分析サービス・オブジェクトに実装されています。

この理由は、ステージを非表示にしたり順序付ける機能を後で追加するためです。

データコレクター

DataCollector は、データベースからデータをクエリする中心的なポイントです。このクラスは常に 1 つのステージでオペレーションを行い、以下のコンポーネントで構成されます:

  • BaseQueryBuilder:
    • 最初のクエリの作成を担当。
    • Stage 固有の設定を扱います: イベントとそのクエリのカスタマイズ。
    • UI からのパラメータ: 日付範囲。
  • Median:BaseQueryBuilder からのクエリを使用して、ステージの期間の中央値を計算します。
  • RecordsFetcher:BaseQueryBuilder からのクエリおよび特定のFinder クラスを使用してステージの関連レコードをロードし、可視性ルールを適用します。
  • DataForDurationChart:散布図チャートの終了時間(終了イベントのタイムスタンプ)と計算された持続時間をロードします。

新しい計算またはクエリのために、DataCollector クラスの新しいメソッド呼び出しとして実装します。

集約された値のストリーム分析バックエンドをサポートするために、これらのクラスはAggregated 名前空間内に再実装されました。

データベースクエリバックエンド

VSAは2つのバックエンドをサポートしています。ライブクエリバックエンドはレガシーであり、いずれ廃止される予定です。

  • “live”: 標準のIssuableFinders を使用します。
  • aggregated”:あらかじめ集約されたデータベーステーブルからデータをクエリ。

高レベルの概要

  • Rails コントローラ (Analytics::CycleAnalytics モジュール):バリューストリーム分析は、analytics ワークスペース内部に実装された JSON エンドポイントを介してデータを公開します。ステージの設定もJSONエンドポイント(CRUD)を実装しています。
  • サービス (Analytics::CycleAnalytics モジュール):Stage 関連のアクションはすべて、それぞれのサービス・オブジェクトに委譲されます。
  • モデル (Analytics::CycleAnalytics モジュール):モデルはStage オブジェクトを永続化するために使用されます。
  • フィーチャークラス (Gitlab::Analytics::CycleAnalytics モジュール):
    • クエリを構成し、機能固有のビジネスロジックを定義します。
    • DataCollector Event StageEvents など。

フロントエンド

プロジェクトVSAは、すべてのユーザーが利用できます:

グループVSAはライセンスユーザーのみ利用可能で、プロジェクトVSAを拡張します:

グループとプロジェクトレベルのVSAフロントエンドは、どちらもVueとVuexで構築され、同様のパターンに従っています:

  • index.js ファイルは、URLクエリパラメータを抽出し、VueアプリとVuexストアを作成し、initialize Vuexアクションをディスパッチします。
  • base.vue ファイルは、各ページの主要コンポーネント、メトリクス、フィルタ、チャート、ステージテーブルのレンダリングに使用されます。

グループVSA Vuexストアは、Vuexモジュールを使用して、チャートのレンダリングに使用される状態とロジックの一部を分離します。

共有コンポーネント

UIの一部は、ステージテーブルやパスなど、プロジェクトVSAとグループVSAの間で共有されます。これらの共有コンポーネントは、プロジェクトVSAのディレクトリapp/assets/javascripts/cycle_analytics/components 、必要に応じてグループレベルのVSAに含まれます。

グループレベルの機能のためのすべてのフロントエンドのコードは、ee/app/assets/javascripts/analytics/cycle_analytics/components にあります。

テスト

たくさんのイベントと可能な組み合わせがあるので、それぞれの組み合わせをテストすることは不可能です。Event クラスを使ったテストケースを少なくともひとつ用意するのがルールです。

新しいEvent を使ってステージのテストケースを書くのは、両方のイベントに対してデータを作成しなければならないので、難しいかもしれません。これを少し簡単にするために、各テストケースはDataCollector を通してステージがテストされるdata_collector_spec.rb に実装されなければなりません。各テストケースは複数のテストになり、以下のケースをカバーします:

  • 異なる親:Group またはProject
  • 異なる計算:Median RecordsFetcher またはDataForDurationChart

VSAフロントエンドは、2つの異なるレベル(インテグレーション、ユニット)で広範囲にテストされています:

  • CapybaraとRSpecによる実際のバックエンドを使ったエンドツーエンドのインテグレーションテスト。
  • 事前に生成されたデータフィクスチャを使った Jest フロントエンドテスト。

開発者のセットアップとテスト

Value Stream Analyticsの実行は、GDK経由で行うことができます。デフォルトでは、プロジェクトレベルの(FOSS) バージョンの機能が表示されます。

GDKが稼働していれば、シードスクリプトを実行してデータを生成できます:

SEED_CYCLE_ANALYTICS=true SEED_VSA=true FILTER=cycle_analytics rake db:seed_fu

データ生成スクリプトは、イシューとマージリクエストデータを含む新しいグループと新しいプロジェクトを作成します(スクリプトの出力を参照してください)。この機能のグループレベルのバージョンを表示するには、GDKインスタンスのライセンスをリクエストする必要があります。

このステップの後、グループ・レベルのバリュー・ストリーム分析ページにアクセスし、バリュー・ストリームとステージを作成できます。データ集計が遅れるため、ステージ作成直後にデータが表示されない場合があります。このプロセスを高速化するには、Railsコンソール (rails c) で次のコマンドを実行します:

Analytics::CycleAnalytics::ReaggregationWorker.new.perform

シードデータ

バリューストリームアナリティクス

バリューストリーム分析のためのデータシード方法については、開発者シードファイルを参照してください。