refactor: use nginx -s reload instead of systemd
- doc: add comment
This commit is contained in:
@@ -9,50 +9,72 @@ import (
|
||||
)
|
||||
|
||||
// InspectionStatus 表示外部系统中某一步的实际结果。
|
||||
//
|
||||
// 它是 Operation.Inspect 的核对结论,Coordinator 据此判断副作用是否真实发生,
|
||||
// 从而决定步骤的最终状态。UNKNOWN 表示暂时无法确认,必须保留意图等待恢复。
|
||||
type InspectionStatus string
|
||||
|
||||
const (
|
||||
InspectionApplied InspectionStatus = "APPLIED"
|
||||
InspectionNotApplied InspectionStatus = "NOT_APPLIED"
|
||||
InspectionUnknown InspectionStatus = "UNKNOWN"
|
||||
InspectionApplied InspectionStatus = "APPLIED" // 外部副作用已确认发生。
|
||||
InspectionNotApplied InspectionStatus = "NOT_APPLIED" // 外部副作用已确认未发生。
|
||||
InspectionUnknown InspectionStatus = "UNKNOWN" // 无法确认副作用是否发生。
|
||||
)
|
||||
|
||||
// Inspection 是执行器通过 inspect、摘要、健康检查等方式得到的实际状态。
|
||||
// Inspection 执行器通过 inspect、摘要、健康检查等方式得到的实际状态。
|
||||
//
|
||||
// 它把对外的状态核对结果封装为统一结构:Status 给出确定/不确定的结论,Result
|
||||
// 保存核对得到的原始证据,用于在步骤成功时持久化。
|
||||
type Inspection struct {
|
||||
Status InspectionStatus
|
||||
Result json.RawMessage
|
||||
Status InspectionStatus // 核对结论。
|
||||
Result json.RawMessage // 核对得到的原始结果载荷,可为空。
|
||||
}
|
||||
|
||||
// Operation 是一个可核对实际结果的外部副作用。
|
||||
// Apply 返回成功只代表调用完成;最终成功必须由 Inspect 确认。
|
||||
// Operation 一个可核对实际结果的外部副作用。
|
||||
//
|
||||
// 实现者负责实际执行外部操作(Apply)并提供事后核对(Inspect)。Apply 返回
|
||||
// 成功只代表调用完成,副作用是否真正生效必须由 Inspect 确认,因此调用方在
|
||||
// Apply 之后无论成功与否都要再次 Inspect,以精确判定外部状态。
|
||||
type Operation interface {
|
||||
Apply(context.Context) error
|
||||
Inspect(context.Context) (Inspection, error)
|
||||
}
|
||||
|
||||
// UncertainStepError 表示当前无法确认外部副作用是否已经发生。
|
||||
// 这种错误必须保留 INTENT_RECORDED,等待恢复流程再次核对。
|
||||
//
|
||||
// 这种错误必须保留 INTENT_RECORDED,等待恢复流程再次核对。它携带事务与步骤
|
||||
// 定位信息及根本原因(Cause),并通过 Unwrap 暴露 Cause 供调用方溯源。
|
||||
type UncertainStepError struct {
|
||||
TransactionID string
|
||||
StepKey string
|
||||
Cause error
|
||||
TransactionID string // 所属事务 ID。
|
||||
StepKey string // 关联步骤键。
|
||||
Cause error // 导致无法确认的根本原因。
|
||||
}
|
||||
|
||||
// Error 返回带事务、步骤与原因的无法确认错误描述。
|
||||
func (e *UncertainStepError) Error() string {
|
||||
return fmt.Sprintf("external step result is uncertain: transaction=%s step=%s: %v", e.TransactionID, e.StepKey, e.Cause)
|
||||
}
|
||||
|
||||
// Unwrap 返回根本原因 Cause,支持 errors.Is/errors.As 溯源。
|
||||
func (e *UncertainStepError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
// Coordinator 串行化单机更新,并实现“先记录意图、再执行、最后 inspect”的步骤协议。
|
||||
//
|
||||
// 它通过容量为 1 的 permit 通道保证同一时刻进程内最多只有一个完整更新流程在
|
||||
// 执行区运行,从而简化外部副作用与数据库状态的一致性推理。所有对外部副作用的
|
||||
// 操作都必须经过 ExecuteStep,以确保意图先落库、结果可核对、崩溃可恢复。
|
||||
type Coordinator struct {
|
||||
store *Store
|
||||
logger *slog.Logger
|
||||
permit chan struct{}
|
||||
store *Store // 持久化存储,记录事务、步骤与事件。
|
||||
logger *slog.Logger // 结构化日志记录器。
|
||||
permit chan struct{} // 容量为 1 的信号量,串行化进入执行区。
|
||||
}
|
||||
|
||||
// NewCoordinator 创建并初始化事务协调器。
|
||||
//
|
||||
// 参数 store 为必填的事务存储,logger 可选,传入 nil 时回退到 slog.Default。
|
||||
// 初始化时向 permit 通道放入一个令牌,表示执行区当前空闲。返回错误仅发生在
|
||||
// store 为 nil 时。
|
||||
func NewCoordinator(store *Store, logger *slog.Logger) (*Coordinator, error) {
|
||||
if store == nil {
|
||||
return nil, errors.New("transaction store is required")
|
||||
@@ -66,6 +88,11 @@ func NewCoordinator(store *Store, logger *slog.Logger) (*Coordinator, error) {
|
||||
}
|
||||
|
||||
// RunExclusive 在一个进程内只允许一个完整更新流程进入执行区。
|
||||
//
|
||||
// 参数 run 是独占执行的更新函数,返回其执行结果;若 run 为 nil 或 ctx 在获得
|
||||
// 执行令牌前已取消,则直接返回相应错误。该方法通过 acquire 令牌、defer 释放
|
||||
// 令牌实现互斥,释放与获取均不阻塞超过令牌容量,因此不存在死锁。返回值为
|
||||
// run 的返回值,或在等待令牌时 ctx 取消导致的错误。
|
||||
func (c *Coordinator) RunExclusive(ctx context.Context, run func(context.Context) error) error {
|
||||
if run == nil {
|
||||
return errors.New("exclusive update function is required")
|
||||
@@ -80,7 +107,14 @@ func (c *Coordinator) RunExclusive(ctx context.Context, run func(context.Context
|
||||
}
|
||||
|
||||
// ExecuteStep 执行或恢复一个外部步骤。
|
||||
// 相同 step key 再次调用时先核对现场,禁止直接重复 Apply。
|
||||
//
|
||||
// 参数 transactionID 是所属事务,intent 描述要执行的外部副作用,operation 提供
|
||||
// 实际执行与核对能力。返回值为该步骤的最终持久化快照以及可能的错误。协议要点:
|
||||
// 相同 step key 再次调用时先核对现场,禁止直接重复 Apply;若意图已成功则直接
|
||||
// 返回;若已失败则依据 inspect 结论决定补记成功或重新打开;若为未执行过的新
|
||||
// 意图,则先记录意图、Apply 后再 Inspect,依据核对结果落库成功或失败。当核对
|
||||
// 结果为 UNKNOWN 或 Inspect 本身出错时,返回 UncertainStepError 并保留
|
||||
// INTENT_RECORDED 等待后续恢复。
|
||||
func (c *Coordinator) ExecuteStep(ctx context.Context, transactionID string, intent StepIntent, operation Operation) (Step, error) {
|
||||
if operation == nil {
|
||||
return Step{}, errors.New("external operation is required")
|
||||
@@ -176,10 +210,18 @@ func (c *Coordinator) ExecuteStep(ctx context.Context, transactionID string, int
|
||||
}
|
||||
}
|
||||
|
||||
// completeApplied 将已确认生效的步骤原子落库为 SUCCEEDED。
|
||||
//
|
||||
// 参数 transactionID 与 stepKey 定位目标步骤,inspection.Result 作为成功结果
|
||||
// 写入。返回更新后的步骤快照及可能的存储错误。
|
||||
func (c *Coordinator) completeApplied(ctx context.Context, transactionID, stepKey string, inspection Inspection) (Step, error) {
|
||||
return c.store.CompleteStep(ctx, transactionID, stepKey, StepSucceeded, inspection.Result, "")
|
||||
}
|
||||
|
||||
// uncertain 记录无法确认外部副作用结果的告警日志并构造 UncertainStepError。
|
||||
//
|
||||
// 参数 transactionID 与 stepKey 用于定位,cause 为根本原因。该方法不改变存储
|
||||
// 状态,仅记录日志并返回错误,交由上层决定是否保留意图等待恢复。
|
||||
func (c *Coordinator) uncertain(ctx context.Context, transactionID, stepKey string, cause error) error {
|
||||
c.logger.WarnContext(ctx, "external step result is uncertain",
|
||||
"transaction_id", transactionID,
|
||||
|
||||
Reference in New Issue
Block a user