Value Stream Analytics development guide

原文:https://docs.gitlab.com/ee/development/value_stream_analytics.html

Value Stream Analytics development guide

值流分析计算域对象上记录的两个任意事件之间的时间,并提供有关持续时间的汇总统计信息.

有关如何在 GitLab 中配置 Value Stream Analytics 的信息,请参阅我们的分析文档 .

Stage

在开发过程中,会发生一些事件,这些事件会移动问题并在不同的进展阶段合并请求,直到认为它们完成为止. 这些阶段可以用Stage模型表示.

示例阶段:

  • Name: Development
  • 开始事件:问题已创建
  • 结束事件:在提交中首先提到的问题
  • Parent: Group: gitlab-org

Events

事件是价值流分析功能的最小构建块. 一个阶段包括两个事件:

  • Start
  • End

这些事件在持续时间计算中起关键作用.

Formula: duration = end_event_time - start_event_time

为了使持续时间计算更加灵活,每个Event都实现为一个单独的类. 他们负责定义将在计算查询中使用的时间戳表达式.

Implementing an Event class

有一些方法需要实现, StageEvent基类非常详细地描述了它们. 最重要的是:

  • object_type
  • timestamp_projection

object_type方法定义将查询哪个域对象以进行计算. 当前允许两种模型:

  • Issue
  • MergeRequest

对于持续时间计算,将使用timestamp_projection方法.

  1. def timestamp_projection
  2. # your timestamp expression comes here
  3. end
  4. # event will use the issue creation time in the duration calculation
  5. def timestamp_projection
  6. Issue.arel_table[:created_at]
  7. end

注意:也可以使用更复杂的表达式(例如,使用COALESCE ). 查看现有事件类作为示例.

在某些情况下,仅定义timestamp_projection方法是不够的. 计算查询应该知道哪个表包含时间戳表达式. 每个Event类负责修改计算查询,以使timestamp_projection起作用. 这通常意味着加入一个附加表.

连接issue_metrics表并使用first_mentioned_in_commit_at列作为时间戳表达式的first_mentioned_in_commit_at

  1. def object_type
  2. Issue
  3. end
  4. def timestamp_projection
  5. IssueMetrics.arel_table[:first_mentioned_in_commit_at]
  6. end
  7. def apply_query_customization(query)
  8. # in this case the query attribute will be based on the Issue model: `Issue.where(...)`
  9. query.joins(:metrics)
  10. end

Validating start and end events

某些开始/结束事件对彼此不”兼容”. 例如:

  • 从”已创建问题”到”已合并请求”:事件类在不同的域模型上定义, object_type方法不同.
  • “问题已关闭”到”问题已创建”:必须先创建问题,然后才能将其关闭.
  • 从”问题已关闭”到”问题已关闭”:持续时间始终为 0.

StageEvents模块描述了允许的start_eventend_event配对( PAIRING_RULES常数). 如果添加了新事件,则需要在该模块中注册它. 要添加新事件:

  1. ENUM_MAPPING添加一个具有唯一编号的条目,该条目将在Stage模型中用作enum .
  2. PAIRING_RULES哈希中定义哪些事件与该事件兼容.

支持的开始/结束事件配对:

图 LR; IssueCreated-> IssueClosed; IssueCreated-> IssueFirstAddedToBoard; IssueCreated-> IssueFirstAssociatedWithMilestone; IssueCreated-> IssueFirstMentionedInCommit; IssueCreated-> IssueLastEdited; IssueCreated-> IssueLabelAdded; IssueCreated-> IssueLabelRemoved; MergeRequestCreated-> MergeRequestMerged; MergeRequestCreated-> MergeRequestClosed; MergeRequestCreated-> MergeRequestFirstDeployedToProduction; MergeRequestCreated-> MergeRequestLastBuildStarted; MergeRequestCreated-> MergeRequestLastBuildFinished; MergeRequestCreated-> MergeRequestLastEdited; MergeRequestCreated-> MergeRequestLabelAdded; MergeRequestCreated-> 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; IssueLabelRemoved-> IssueClosed; IssueFirstAddedToBoard-> IssueClosed; IssueFirstAddedToBoard-> IssueFirstAssociatedWithMilestone; IssueFirstAddedToBoard-> IssueFirstMentionedInCommit; IssueFirstAddedToBoard-> IssueLastEdited; IssueFirstAddedToBoard-> IssueLabelAdded; IssueFirstAddedToBoard-> IssueLabelRemoved; IssueFirstAssociatedWithMilestone-> IssueClosed; IssueFirstAssociatedWithMilestone-> IssueFirstAddedToBoard; IssueFirstAssociatedWithMilestone-> IssueFirstMentionedInCommit; IssueFirstAssociatedWithMilestone-> IssueLastEdited; IssueFirstAssociatedWithMilestone-> IssueLabelAdded; IssueFirstAssociatedWithMilestone-> IssueLabelRemoved; IssueFirstMentionedInCommit-> IssueClosed; IssueFirstMentionedInCommit-> IssueFirstAssociatedWithMilestone; IssueFirstMentionedInCommit-> IssueFirstAddedToBoard; IssueFirstMentionedInCommit-> IssueLastEdited; IssueFirstMentionedInCommit-> IssueLabelAdded; IssueFirstMentionedInCommit->已删除 IssueLabel; 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; MergeRequestLabelRemoved-> MergeRequestLabelAdded; MergeRequestLabelRemoved-> MergeRequestLabelRemoved;

Parent

团队和组织可能会定义自己的软件构建方式,因此阶段可能完全不同. 对于每个阶段,都需要定义一个父对象.

目前支持的父母:

  • Project
  • Group

How parent relationship it work

  1. 用户导航到价值流分析页面.
  2. 用户选择一个组.
  3. 后端将加载选定组的已定义阶段.
  4. 对阶段的添加和修改将仅保留在所选组中.

Default stages

价值流分析的原始实施定义了 7 个阶段. 每个父母都可以使用这些阶段,但是无法更改这些阶段. 为了提高效率并减少创建的记录数,默认阶段被表示为内存中对象(不持久). 当用户首次创建自定义阶段时,所有阶段都将保留. 此行为在价值流分析服务对象中实现. 这样做的原因是我们希望稍后添加隐藏和订购阶段的功能.

Data Collector

DataCollector是从数据库查询数据的中心点. 该类始终在单个阶段上运行,并且由以下组件组成:

  • BaseQueryBuilder:
    • 负责编写初始查询.
    • 处理特定于Stage配置:事件及其查询自定义.
    • 来自用户界面的参数:日期范围.
  • Median :使用BaseQueryBuilder的查询计算一个阶段的中位数持续时间.
  • RecordsFetcher :使用来自BaseQueryBuilder的查询和特定的Finder类加载阶段的相关记录,以应用可见性规则.
  • DataForDurationChart :加载散点图的带有完成时间(结束事件时间戳)的计算的持续时间.

对于新的计算或查询,可将其实现为DataCollector类中的新方法调用.

Database query

数据库查询的结构:

  1. SELECT (customized by: Median or RecordsFetcher or DataForDurationChart)
  2. FROM OBJECT_TYPE (Issue or MergeRequest)
  3. INNER JOIN (several JOIN statements, depending on the events)
  4. WHERE
  5. (Filter by the PARENT model, example: filter Issues from Project A)
  6. (Date range filter based on the OBJECT_TYPE.created_at)
  7. (Check if the START_EVENT is earlier than END_EVENT, preventing negative duration)

MedianSELECT语句的结构:

  1. SELECT (calculate median from START_EVENT_TIME-END_EVENT_TIME)

用于DataForDurationChartSELECT语句的DataForDurationChart

  1. SELECT (START_EVENT_TIME-END_EVENT_TIME) as duration, END_EVENT.timestamp

High-level overview

  • Rails 控制器( Analytics::CycleAnalytics模块):值流分析通过 JSON 端点公开其数据,该端点在analytics工作区中实现. 配置阶段还实现 JSON 端点(CRUD).
  • 服务( Analytics::CycleAnalytics模块):所有与Stage相关的操作都将委派给相应的服务对象.
  • 模型( Analytics::CycleAnalytics模块):模型用于持久化Stage对象ProjectStageGroupStage .
  • 要素类( Gitlab::Analytics::CycleAnalytics模块):
    • 负责撰写查询并定义特定于功能的业务逻辑.
    • DataCollectorEventStageEvents等.

Testing

由于我们有很多事件和可能的配对,因此无法测试每个配对. 规则是至少要有一个使用Event类的测试用例.

使用新Event为阶段编写测试用例可能会遇到挑战,因为必须为两个事件都创建数据. 为了使此过程更简单,必须在data_collector_spec.rb中实现每个测试用例,在该data_collector_spec.rb中,通过DataCollector对该阶段进行测试. 每个测试用例都将变成多个测试,涵盖以下情况:

  • 不同的父母: GroupProject
  • 不同的计算方式: MedianRecordsFetcherDataForDurationChart