86 lines
6.1 KiB
Go
86 lines
6.1 KiB
Go
package main
|
||
|
||
import (
|
||
"math"
|
||
"runtime"
|
||
"sort"
|
||
"time"
|
||
|
||
orbit "git.yuntuo.hk/k2safe/orbit/sdk/go"
|
||
)
|
||
|
||
// distribution 只统计完成核验的实例;空集合的百分位保持null。
|
||
type distribution struct {
|
||
Count int `json:"count"` // Count 纳入本项统计的样本数。
|
||
P50 *float64 `json:"p50_ms"` // P50 最近秩中位数,毫秒。
|
||
P95 *float64 `json:"p95_ms"` // P95 最近秩95百分位,毫秒。
|
||
P99 *float64 `json:"p99_ms"` // P99 最近秩99百分位,毫秒。
|
||
Max *float64 `json:"max_ms"` // Max 纳入样本的最大毫秒数。
|
||
}
|
||
|
||
// workflowReport 是一次有界流程测量结果,不是容量承诺或生产高可用验收。
|
||
type workflowReport struct {
|
||
RunID string `json:"run_id"` // RunID 本次定义、实例和处理器身份的隔离前缀。
|
||
GoVersion string `json:"go_version"` // GoVersion 实际编译工具链版本。
|
||
GOOS string `json:"goos"` // GOOS 负载进程操作系统。
|
||
GOARCH string `json:"goarch"` // GOARCH 负载进程架构。
|
||
StartedAt time.Time `json:"started_at"` // StartedAt 从预检开始的本机时间。
|
||
AdmissionFinishedAt time.Time `json:"admission_finished_at"` // AdmissionFinishedAt 实例受理组完成时间。
|
||
FinishedAt time.Time `json:"finished_at"` // FinishedAt 核验及Worker收尾完成时间。
|
||
Requests int `json:"requests"` // Requests 计划启动的流程实例数。
|
||
Concurrency int `json:"concurrency"` // Concurrency 启动和核验请求的最大并发。
|
||
Workers int `json:"workers"` // Workers 本进程独立WSS Worker数。
|
||
Capacity int `json:"capacity_per_worker"` // Capacity 每个Worker的执行槽上限。
|
||
RunTimeoutMS int64 `json:"run_timeout_ms"` // RunTimeoutMS 整次测量的截止时间。
|
||
PollIntervalMS int64 `json:"poll_interval_ms"` // PollIntervalMS 流程状态轮询间隔。
|
||
HTTPTimeoutMS int64 `json:"http_timeout_ms"` // HTTPTimeoutMS 单次HTTP交换截止时间。
|
||
Queue *orbit.Queue `json:"queue,omitempty"` // Queue 预检时的队列快照,不修改配置。
|
||
DefinitionID string `json:"definition_id,omitempty"` // DefinitionID 本次新建的稳定定义身份。
|
||
VersionID string `json:"version_id,omitempty"` // VersionID 本次发布的不可变版本身份。
|
||
Attempted int `json:"attempted"` // Attempted 实际发起的启动调用数。
|
||
NotSubmitted int `json:"not_submitted"` // NotSubmitted 截止前未发起的实例数。
|
||
Accepted int `json:"accepted"` // Accepted 收到并核验唯一启动回执的实例数。
|
||
AdmissionErrors int `json:"admission_errors"` // AdmissionErrors 明确拒绝或回执不合法的实例数。
|
||
VerifiedSuccess int `json:"verified_success"` // VerifiedSuccess 流程、步骤、任务、来源和结果均核验通过数。
|
||
Unverified int `json:"unverified"` // Unverified 已受理但没有完成全部核验的实例数。
|
||
HandlerCalls int `json:"handler_calls"` // HandlerCalls 有效处理器调用总数,两个分支各一次。
|
||
DuplicateInvocations int `json:"duplicate_invocations"` // DuplicateInvocations 超过两个分支预期的额外调用数。
|
||
WorkerStarted uint64 `json:"worker_started"` // WorkerStarted SDK本进程领取并启动的任务数。
|
||
Reconnects uint64 `json:"worker_reconnects"` // Reconnects SDK重连次数。
|
||
Renewals uint64 `json:"worker_renewals_confirmed"` // Renewals SDK确认的租约续期次数。
|
||
Admission distribution `json:"admission"` // Admission 启动请求本机往返耗时。
|
||
Completion distribution `json:"completion"` // Completion 数据库实例创建至完成的服务端耗时。
|
||
Errors map[string]int `json:"errors"` // Errors 稳定错误分类,不写入凭据、SQL或请求正文。
|
||
Complete bool `json:"complete"` // Complete 仅全部实例和两倍处理器调用逐项通过核验时为真。
|
||
}
|
||
|
||
func newReport(cfg config, runID string) workflowReport {
|
||
return workflowReport{RunID: runID, GoVersion: runtime.Version(), GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, StartedAt: time.Now().UTC(), Requests: cfg.Requests, NotSubmitted: cfg.Requests, Concurrency: cfg.Concurrency, Workers: cfg.Workers, Capacity: cfg.Capacity, RunTimeoutMS: cfg.RunTimeout.Milliseconds(), PollIntervalMS: cfg.PollInterval.Milliseconds(), HTTPTimeoutMS: cfg.Timeout.Milliseconds(), Errors: map[string]int{}}
|
||
}
|
||
|
||
func (r workflowReport) finish() workflowReport {
|
||
for code, count := range r.Errors {
|
||
if count == 0 {
|
||
delete(r.Errors, code)
|
||
}
|
||
}
|
||
r.FinishedAt = time.Now().UTC()
|
||
r.Unverified = r.Accepted - r.VerifiedSuccess
|
||
r.Complete = r.Attempted == r.Requests && r.NotSubmitted == 0 && r.AdmissionErrors == 0 && r.Accepted == r.Requests && r.VerifiedSuccess == r.Requests && r.HandlerCalls == 2*r.Requests && r.WorkerStarted == uint64(2*r.Requests) && r.DuplicateInvocations == 0 && len(r.Errors) == 0
|
||
return r
|
||
}
|
||
|
||
func distributionOf(values []time.Duration) distribution {
|
||
d := distribution{Count: len(values)}
|
||
if len(values) == 0 {
|
||
return d
|
||
}
|
||
sort.Slice(values, func(i, j int) bool { return values[i] < values[j] })
|
||
percentile := func(q float64) *float64 {
|
||
v := float64(values[int(math.Ceil(float64(len(values))*q))-1]) / float64(time.Millisecond)
|
||
return &v
|
||
}
|
||
d.P50, d.P95, d.P99, d.Max = percentile(.5), percentile(.95), percentile(.99), percentile(1)
|
||
return d
|
||
}
|