130 lines
5.3 KiB
Go
130 lines
5.3 KiB
Go
package observability
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"runtime"
|
|
"time"
|
|
|
|
"git.yuntuo.hk/k2safe/orbit/internal/buildinfo"
|
|
)
|
|
|
|
// RuntimeConfig 是节点启动后实际生效的后台控制参数。
|
|
// 它只用于固定基数观测,不参与业务授权或持久化状态判断。
|
|
type RuntimeConfig struct {
|
|
WorkflowTickInterval time.Duration
|
|
ScheduleTickInterval time.Duration
|
|
RecoveryTickInterval time.Duration
|
|
BatchLimit int
|
|
ShutdownTimeout time.Duration
|
|
WorkerConnectionLimit int // Worker WebSocket 单节点连接上限。
|
|
EventConnectionLimit int // Event WebSocket 单节点连接上限。
|
|
DispatchWorkers int // 每个 Hub 同时执行数据库领取调用的协程上限。
|
|
DispatchCapacity int // 每个 Hub 领取调用的有界内存排队槽位。
|
|
}
|
|
|
|
// RecordRuntimeConfig records the effective bounded controller settings.
|
|
func (r *Registry) RecordRuntimeConfig(config RuntimeConfig) {
|
|
if r == nil || config.WorkflowTickInterval <= 0 || config.ScheduleTickInterval <= 0 || config.RecoveryTickInterval <= 0 || config.BatchLimit <= 0 || config.ShutdownTimeout <= 0 || config.WorkerConnectionLimit <= 0 || config.EventConnectionLimit <= 0 || config.DispatchWorkers <= 0 || config.DispatchCapacity <= 0 {
|
|
return
|
|
}
|
|
r.SetGauge("orbit_runtime_tick_interval_seconds", config.WorkflowTickInterval.Seconds(), Labels{"component": "workflow"})
|
|
r.SetGauge("orbit_runtime_tick_interval_seconds", config.ScheduleTickInterval.Seconds(), Labels{"component": "schedule"})
|
|
r.SetGauge("orbit_runtime_tick_interval_seconds", config.RecoveryTickInterval.Seconds(), Labels{"component": "execution_recovery"})
|
|
r.SetGauge("orbit_runtime_batch_limit", float64(config.BatchLimit), nil)
|
|
r.SetGauge("orbit_shutdown_timeout_seconds", config.ShutdownTimeout.Seconds(), nil)
|
|
r.SetGauge("orbit_worker_connection_limit", float64(config.WorkerConnectionLimit), nil)
|
|
r.SetGauge("orbit_event_connection_limit", float64(config.EventConnectionLimit), nil)
|
|
r.SetGauge("orbit_dispatch_workers", float64(config.DispatchWorkers), nil)
|
|
r.SetGauge("orbit_dispatch_capacity", float64(config.DispatchCapacity), nil)
|
|
}
|
|
|
|
// RecordDraining records whether this process has entered graceful shutdown.
|
|
// The value is a fixed, node-local gauge: 0 means serving and 1 means draining.
|
|
func (r *Registry) RecordDraining(draining bool) {
|
|
if r == nil {
|
|
return
|
|
}
|
|
value := 0.0
|
|
if draining {
|
|
value = 1
|
|
}
|
|
r.SetGauge("orbit_process_draining", value, nil)
|
|
}
|
|
|
|
// initializeProcess is called once by the registry constructor.
|
|
func (r *Registry) initializeProcess() {
|
|
if r == nil {
|
|
return
|
|
}
|
|
r.SetGauge("orbit_process_start_time_seconds", float64(r.started.Unix()), nil)
|
|
r.RecordDraining(false)
|
|
build := buildinfo.Current()
|
|
r.SetGauge("orbit_build_info", 1, Labels{"go_version": build.GoVersion, "version": build.Version, "revision": build.Revision})
|
|
r.RecordProcess()
|
|
for _, component := range components {
|
|
labels := Labels{"component": component}
|
|
r.SetGauge("orbit_runtime_batch_last_success_timestamp_seconds", 0, labels)
|
|
for _, outcome := range []string{"success", "error", "cancelled"} {
|
|
r.AddCounter("orbit_runtime_batch_total", 0, Labels{"component": component, "outcome": outcome})
|
|
}
|
|
}
|
|
for _, route := range []string{"worker_connect", "event_connect"} {
|
|
r.SetGauge("orbit_websocket_connections", 0, Labels{"route": route})
|
|
r.AddCounter("orbit_websocket_connections_total", 0, Labels{"route": route})
|
|
}
|
|
}
|
|
func (r *Registry) RecordProcess() {
|
|
if r == nil {
|
|
return
|
|
}
|
|
r.SetGauge("orbit_process_uptime_seconds", time.Since(r.started).Seconds(), nil)
|
|
r.SetGauge("orbit_go_goroutines", float64(runtime.NumGoroutine()), nil)
|
|
}
|
|
func (r *Registry) RecordBatch(component string, started time.Time, err error) {
|
|
if r == nil {
|
|
return
|
|
}
|
|
outcome := "success"
|
|
if err != nil {
|
|
outcome = "error"
|
|
if errors.Is(err, context.Canceled) {
|
|
outcome = "cancelled"
|
|
}
|
|
}
|
|
r.AddCounter("orbit_runtime_batch_total", 1, Labels{"component": component, "outcome": outcome})
|
|
r.ObserveHistogram("orbit_runtime_batch_duration_seconds", time.Since(started).Seconds(), Labels{"component": component})
|
|
if err == nil {
|
|
r.SetGauge("orbit_runtime_batch_last_success_timestamp_seconds", float64(time.Now().Unix()), Labels{"component": component})
|
|
}
|
|
}
|
|
|
|
// RecordDatabase reads the local connection-pool snapshot; it issues no SQL.
|
|
func (r *Registry) RecordDatabase(s sql.DBStats) {
|
|
if r == nil {
|
|
return
|
|
}
|
|
r.SetGauge("orbit_db_connections_open", float64(s.OpenConnections), nil)
|
|
r.SetGauge("orbit_db_connections_in_use", float64(s.InUse), nil)
|
|
r.SetGauge("orbit_db_connections_idle", float64(s.Idle), nil)
|
|
r.SetGauge("orbit_db_connections_max_open", float64(s.MaxOpenConnections), nil)
|
|
r.SetCounter("orbit_db_wait_count_total", float64(s.WaitCount), nil)
|
|
r.SetCounter("orbit_db_wait_duration_seconds_total", s.WaitDuration.Seconds(), nil)
|
|
}
|
|
|
|
// TransactionStats 是本进程提交前事务重试的累计观测值。
|
|
type TransactionStats struct {
|
|
Retries uint64 // 已实际执行的重试次数。
|
|
Exhausted uint64 // 达到重试上限的事务次数。
|
|
}
|
|
|
|
// RecordTransactionStats records bounded database transaction retry counters.
|
|
func (r *Registry) RecordTransactionStats(stats TransactionStats) {
|
|
if r == nil {
|
|
return
|
|
}
|
|
r.SetCounter("orbit_db_transaction_retries_total", float64(stats.Retries), nil)
|
|
r.SetCounter("orbit_db_transaction_retry_exhausted_total", float64(stats.Exhausted), nil)
|
|
}
|