Files

97 lines
7.2 KiB
Go
Raw Permalink 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 纳入样本的最大毫秒数。
}
// instanceReport 保存每个启动请求的原身份,便于未知结果按原键核对。
type instanceReport struct {
Sequence int `json:"sequence"` // Sequence 从零开始的请求序号。
Key string `json:"key"` // Key 启动幂等键,同时作为业务键。
ID string `json:"id,omitempty"` // ID 已取得的实例身份;为空不证明未提交。
Code string `json:"admission_error,omitempty"` // Code 受理错误分类,不包含原异常正文。
Verified bool `json:"verified"` // Verified 是否通过持久结果核验。
}
// workflowReport 是一次有界流程测量结果,不是容量承诺或生产高可用验收。
type workflowReport struct {
Instances []instanceReport `json:"instances"` // Instances 已尝试启动的请求身份,包含失败或未知结果。
UnconfirmedInvocations int `json:"unconfirmed_invocations"` // UnconfirmedInvocations 尚未关联到已核验分支任务/尝试的调用数。
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{Instances: []instanceReport{}, 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 && r.UnconfirmedInvocations == 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
}