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
方法.
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
列作为时间戳表达式的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
Validating start and end events
某些开始/结束事件对彼此不”兼容”. 例如:
- 从”已创建问题”到”已合并请求”:事件类在不同的域模型上定义,
object_type
方法不同. - “问题已关闭”到”问题已创建”:必须先创建问题,然后才能将其关闭.
- 从”问题已关闭”到”问题已关闭”:持续时间始终为 0.
StageEvents
模块描述了允许的start_event
和end_event
配对( PAIRING_RULES
常数). 如果添加了新事件,则需要在该模块中注册它. 要添加新事件:
- 在
ENUM_MAPPING
添加一个具有唯一编号的条目,该条目将在Stage
模型中用作enum
. - 在
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
- 用户导航到价值流分析页面.
- 用户选择一个组.
- 后端将加载选定组的已定义阶段.
- 对阶段的添加和修改将仅保留在所选组中.
Default stages
价值流分析的原始实施定义了 7 个阶段. 每个父母都可以使用这些阶段,但是无法更改这些阶段. 为了提高效率并减少创建的记录数,默认阶段被表示为内存中对象(不持久). 当用户首次创建自定义阶段时,所有阶段都将保留. 此行为在价值流分析服务对象中实现. 这样做的原因是我们希望稍后添加隐藏和订购阶段的功能.
Data Collector
DataCollector
是从数据库查询数据的中心点. 该类始终在单个阶段上运行,并且由以下组件组成:
BaseQueryBuilder
:- 负责编写初始查询.
- 处理特定于
Stage
配置:事件及其查询自定义. - 来自用户界面的参数:日期范围.
Median
:使用BaseQueryBuilder
的查询计算一个阶段的中位数持续时间.RecordsFetcher
:使用来自BaseQueryBuilder
的查询和特定的Finder
类加载阶段的相关记录,以应用可见性规则.DataForDurationChart
:加载散点图的带有完成时间(结束事件时间戳)的计算的持续时间.
对于新的计算或查询,可将其实现为DataCollector
类中的新方法调用.
Database query
数据库查询的结构:
SELECT (customized by: Median or RecordsFetcher or DataForDurationChart)
FROM OBJECT_TYPE (Issue or MergeRequest)
INNER JOIN (several JOIN statements, depending on the events)
WHERE
(Filter by the PARENT model, example: filter Issues from Project A)
(Date range filter based on the OBJECT_TYPE.created_at)
(Check if the START_EVENT is earlier than END_EVENT, preventing negative duration)
Median
的SELECT
语句的结构:
SELECT (calculate median from START_EVENT_TIME-END_EVENT_TIME)
用于DataForDurationChart
的SELECT
语句的DataForDurationChart
:
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
对象ProjectStage
和GroupStage
. - 要素类(
Gitlab::Analytics::CycleAnalytics
模块):- 负责撰写查询并定义特定于功能的业务逻辑.
DataCollector
,Event
,StageEvents
等.
Testing
由于我们有很多事件和可能的配对,因此无法测试每个配对. 规则是至少要有一个使用Event
类的测试用例.
使用新Event
为阶段编写测试用例可能会遇到挑战,因为必须为两个事件都创建数据. 为了使此过程更简单,必须在data_collector_spec.rb
中实现每个测试用例,在该data_collector_spec.rb
中,通过DataCollector
对该阶段进行测试. 每个测试用例都将变成多个测试,涵盖以下情况:
- 不同的父母:
Group
或Project
- 不同的计算方式:
Median
,RecordsFetcher
或DataForDurationChart