Files
orbit/sdk/go/cmd/orbit-workflow-load/report.go

86 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}