650.md 11.2 KB
Newer Older
Lab机器人's avatar
readme  
Lab机器人 已提交
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
# Value Stream Analytics development guide

> 原文:[https://docs.gitlab.com/ee/development/value_stream_analytics.html](https://docs.gitlab.com/ee/development/value_stream_analytics.html)

*   [Stage](#stage)
    *   [Events](#events)
        *   [Implementing an `Event` class](#implementing-an-event-class)
    *   [Validating start and end events](#validating-start-and-end-events)
    *   [Parent](#parent)
        *   [How parent relationship it work](#how-parent-relationship-it-work)
    *   [Default stages](#default-stages)
*   [Data Collector](#data-collector)
*   [Database query](#database-query)
*   [High-level overview](#high-level-overview)
*   [Testing](#testing)

# Value Stream Analytics development guide[](#value-stream-analytics-development-guide "Permalink")

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

有关如何在 GitLab 中配置 Value Stream Analytics 的信息,请参阅我们的[分析文档](../user/analytics/value_stream_analytics.html) .

## Stage[](#stage "Permalink")

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

示例阶段:

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

### Events[](#events "Permalink")

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

*   Start
*   End

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

Formula: `duration = end_event_time - start_event_time`

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

#### Implementing an `Event` class[](#implementing-an-event-class "Permalink")

有一些方法需要实现, `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[](#validating-start-and-end-events "Permalink")

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

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

`StageEvents`模块描述了允许的`start_event``end_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[](#parent "Permalink")

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

目前支持的父母:

*   `Project`
*   `Group`

#### How parent relationship it work[](#how-parent-relationship-it-work "Permalink")

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

### Default stages[](#default-stages "Permalink")

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

## Data Collector[](#data-collector "Permalink")

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

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

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

## Database query[](#database-query "Permalink")

数据库查询的结构:

```
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[](#high-level-overview "Permalink")

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

## Testing[](#testing "Permalink")

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

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

*   不同的父母: `Group``Project`
*   不同的计算方式: `Median``RecordsFetcher``DataForDurationChart`