reliable

package
v1.7.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

reliable — 可靠消费内核

jxt-core 的可靠消费内核:一张 event_consumption 表实现幂等 + 死信 + 重投调度,五状态机 + fencing token,at-least-once 语义。根包零第三方依赖(J2)。

目录

快速开始

最小接入:建表 → 实现 handler + registry → 启动重放调度器。代码 illustrative,签名以代码为准。

package main

import (
	"context"
	"time"

	"github.com/ChenBigdata421/jxt-core/sdk/pkg/reliable"
	"github.com/ChenBigdata421/jxt-core/sdk/pkg/reliable/replay"
	"github.com/ChenBigdata421/jxt-core/sdk/pkg/reliable/store/mysql"
	"gorm.io/gorm"
)

// 1) handler:声明 replay 安全类别 + 是否需要聚合级串行(aggregate gate)。
type mediaHandler struct{}

func (mediaHandler) HandlerID() reliable.HandlerID       { return "media" }
func (mediaHandler) ReplaySafety() reliable.ReplaySafety { return reliable.ReplayIdempotent }
func (mediaHandler) RequiresAggregateGate() bool         { return false }
func (mediaHandler) Handle(ctx context.Context, payload []byte, meta reliable.DeliveryMeta) error {
	// 处理 payload;返回 reliable.ErrRetryLater 让路,其它错误按失败结算。
	return nil
}

// 2) registry:scheduler 凭 HandlerID 定位 handler。
type registry struct{ hs []replay.HandlerInfo }

func (r *registry) Lookup(id reliable.HandlerID) (replay.HandlerInfo, bool) {
	for _, h := range r.hs {
		if h.HandlerID == id {
			return h, true
		}
	}
	return replay.HandlerInfo{}, false
}
func (r *registry) All() []replay.HandlerInfo { return r.hs }

func run(ctx context.Context, db *gorm.DB) error {
	// 3) 建表(DSN 须 multiStatements=true)。
	if err := mysql.Migration()(db); err != nil {
		return err
	}
	st := mysql.NewStore(db) // event_consumption 读写

	// 4) 启动重放调度器,驱动 RETRY_SCHEDULED 队列。
	sch := replay.NewScheduler(st, db, &registry{hs: []replay.HandlerInfo{
		{HandlerID: "media", ReplaySafety: reliable.ReplayIdempotent, Handler: mediaHandler{}},
	}}, nil, nil) // metrics / alerter 传 nil → 内部回退 NoOp
	return sch.Run(ctx, 5*time.Second) // 每 5s 一轮 tick
}

生产用法还需:用 mysql.NewQuarantineStore(db) 接不可解码坏消息的隔离区;起 lease.Runner(见包结构)观测租约孤儿;注入真实的 ConsumptionMetrics / Alerter

核心概念

五状态机(状态与合法转移以 state.goStatus* 常量 + legalTransitions)为准;下图为示意):

stateDiagram-v2
    [*] --> PROCESSING: TryClaim
    PROCESSING --> SUCCEEDED: MarkSucceeded
    PROCESSING --> RETRY_SCHEDULED: MarkFailed(retryable)
    PROCESSING --> DEAD_LETTER: MarkFailed(poison/unsafe)
    RETRY_SCHEDULED --> PROCESSING: ClaimForReplay
    RETRY_SCHEDULED --> DEAD_LETTER: MoveToDeadLetter
    DEAD_LETTER --> RETRY_SCHEDULED: ScheduleReplay(双人授权)
    DEAD_LETTER --> DISCARDED: Discard
    DEAD_LETTER --> SUCCEEDED: 人工结案
    SUCCEEDED --> [*]
    DISCARDED --> [*]
  • 一张表三用(M1)event_consumption 同时承担幂等去重、死信、重投调度,无需额外队列表。
  • fencing token:每次占位发新的 ClaimTokenclaim_id),结算须出示它。这让「UPDATE 0 行」从设计边界变成可判定异常——能区分「已被别实例接管」与「真失败」。
  • at-least-once + 租约自愈:handler 处理期间持租约;进程崩溃留下 PROCESSING 孤儿行,由 lease.Runner 观测 + broker 重投恢复(可能重复执行,故 handler 须标 ReplaySafety)。
  • aggregate gateRequiresAggregateGate=true 的 handler,重放前先抢 (tenant, aggregate) 级 lease,保证同聚合串行——对非幂等 handler 尤其关键。
  • replay safety:每个 handler 声明 ReplayUnsafe/ReplayNeedsTxClaim/ReplayIdempotent(默认 ReplayUnsafe,有进程外副作用),决定能否自动重放(细节见 safety.go)。

包结构

职责 文档
reliable(根) 契约类型 + 纯函数(Status/Key/ClaimInput/Decision/ReplaySafety/ErrorClass/分类器雏形)。零第三方依赖。 doc.go
reliable/store Store / QuarantineStore 接口 + Row / QuarantineRow store/doc.go
reliable/store/gormshared MySQL / PostgreSQL 共享 GORM 实现。 store/gormshared/doc.go
reliable/store/mysql reliable/store/postgres 方言 migration SQL + classifier + NewStore store/{mysql,postgres}/doc.go
reliable/store/repotest 双方言 conformance 套件(准入门禁)。 store/repotest/doc.go
reliable/replay eligible-head 重放调度器(Scheduler)。 replay/doc.go
reliable/lease 租约孤儿观测 runner。 lease/doc.go

关键不变量

  • 一张表三用(M1)event_consumption = 幂等 + 死信 + 重投调度。
  • TryClaim 独立提交NewStore 构造期派生独立 session(§3.3);Mark*/AdvanceDue 等显式接收调用方 *gorm.DB,可加入业务事务(M14)。
  • fencingMark* 须出示 claim_idClaimToken);0 行 = 可判定异常,非设计边界(M3)。
  • §2.4 列清空规则:状态迁移时按不变量表清 ownership / 错误字段。
  • aggregate gate 前置:抢不到 gate 整行不动、attempt 不增,下一周期重试。
  • 多租户隔离GetByID/List/MarkResolved 强制 tenant 作用域;部署模型为每租户独立库(S3)。
  • CAS 写路径传播 res.Error:DB 错误/ctx 取消不被伪装成 ErrConflict(先查 *gorm.DB.Error 再看 RowsAffected)。

文档地图

  • doc.go(8 个,根 + 7 子包):每个类型的契约与设计注记,参考型文档主体。
  • PR2_SCOPE.md(仓库根):PR-2 范围、设计决策、PR-3 / PR-7 carry-over。
  • §spec(opus5-RCC-v2 §1~§8):代码注释中大量 §N 引用指向的外部规范;本 README 不复述。
  • conformance 套件reliable/store/repotest——双方言(MySQL/PostgreSQL)下的行为真相源。

Documentation

Overview

Package reliable 提供可靠消费的状态机内核与持久化抽象(opus5-RCC-v2 §1~§8)。

根包零第三方依赖(J2):只含纯契约类型与纯函数。gorm/数据库驱动/prometheus/gin 全部在 store 子树与消费服务侧。

关键不变量(由 store/repotest 在 MySQL/PostgreSQL 双方言上验证):

  • 一张 event_consumption 表 = 幂等 + 死信 + 重投调度(M1)。
  • 五状态单状态机:PROCESSING | SUCCEEDED | RETRY_SCHEDULED | DEAD_LETTER | DISCARDED(§3)。
  • TryClaim 独立提交、构造期保证(NewStore 派生独立 session,§3.3);Mark* 显式接收 *gorm.DB(M14)。
  • claim_id(ClaimToken)校验让 0 行从「设计边界」变成「可判定异常」(M3/edge #6/#6b)。

Index

Constants

View Source
const (
	MetricDeadLetterTotal         = "consumption_dead_letter_total"
	MetricRecordFailureTotal      = "consumption_record_failure_total"
	MetricAnomalyTotal            = "consumption_anomaly_total"
	MetricPartitionStalledSecs    = "consumption_partition_stalled_seconds"
	MetricPendingCount            = "consumption_pending_count"
	MetricReplayBlockedCount      = "consumption_replay_blocked_count"
	MetricOutboxDeadLetteredTotal = "outbox_dead_lettered_total"
)

指标名常量(§8.4/§10,M13 规范)。必须与 §10 告警表逐一对应。

View Source
const (
	LabelHandler   = "handler"
	LabelTenant    = "tenant"
	LabelErrClass  = "error_class"
	LabelKind      = "kind"
	LabelStatus    = "status"
	LabelTopic     = "topic"
	LabelPartition = "partition"
)

标签常量(M13)。

View Source
const (
	DefaultBackoffBase = time.Second
	DefaultBackoffCap  = time.Hour
)

DefaultBackoffBase / DefaultBackoffCap(§6.2 起点 1s / 上限 1h)。

View Source
const DefaultMaxAttempts = 5

DefaultMaxAttempts 是 attempt 耗尽终点的默认上限(§6.2)。MarkFailed 的 ShouldDeadLetter 与 replay scheduler 的 ErrRetryLater 让路终点共用同一常量——失败路径与让路路径的「重试天花板」必须对称: 否则一个永远返回 ErrRetryLater 的 handler 会以 ~1h 间隔无限重试、永不进死信(scheduler.processOne 的 InvokeRetryLater 分支据此把 attempt≥max 升级为 DEAD_LETTER + REPLAY_DEFER_EXHAUSTED)。服务可按 handler 覆盖;PR-3 的装饰器应让 MarkFailed 与 scheduler 取同一值。

Variables

View Source
var ErrConflict = errors.New("reliable: conflicting state")

ErrConflict 终态 CAS 版本不符(运维 API expected_row_version 不匹配)或 RecordTerminal 遇现存 PROCESSING, 或 ScheduleReplay 的 requester==approver(双人确认违规)。

View Source
var ErrDuplicateKey = errors.New("reliable: duplicate key violation")

ErrDuplicateKey 表示某次写入撞了唯一索引。它只声明事实,不声明该冲突是否等于幂等命中 —— handler 必须自行核实约束名属本次事件的幂等键、读回已有行、比对内容一致后,才能包成 ErrSkip(§5)。 取代 evidence-management 与 process-management 各自的 service.ErrDuplicateKey 分裂副本(PR1_SCOPE C5)。

View Source
var ErrIllegalTransition = errors.New("reliable: illegal status transition")

ErrIllegalTransition 由 store 在检测到非法转移时返回(纵深防御)。

View Source
var ErrNotPermitted = errors.New("reliable: replay not permitted")

ErrNotPermitted 重放时 CanAutoReplay=false 命中(纵深防御,正常不应触发;触发即 §6.1 矩阵有漏洞)。

View Source
var ErrNotSelfReplayable = errors.New("reliable: row not self-replayable")

ErrNotSelfReplayable 行的 payload IS NULL,靠 broker 重投;若该行已 ACK 则 broker 不再投,需人工介入。

View Source
var ErrRetryLater = errors.New("reliable: retry later")

ErrRetryLater 由 TryClaim 在 AlreadyProcessing 分支返回:他人持有有效租约,本次不落库、不 ACK, 交还 broker 稍后重投(§3.1)。

View Source
var ErrSkip = errors.New("reliable: idempotent skip")

ErrSkip 由 handler 在第 1 级显式声明:本次事件已被认定为幂等命中(如核实后的唯一冲突), 装饰器按成功终结(§4 Phase B 的 MarkSucceeded 分支)。第 2 级 driver classifier 不再兜底判 Skip。

Functions

func AdvanceAttempt

func AdvanceAttempt(current int) int

AdvanceAttempt 把 attempt 推进到下一次业务执行(RETRY_SCHEDULED→PROCESSING 时调用)。

func Backoff

func Backoff(attempt int, base, cap time.Duration, jitterFraction float64) time.Duration

Backoff 计算下次重试的退避时长(§6.2)。纯函数:jitterFraction ∈ [0,1) 由调用方提供。 公式:base × 2^(attempt-1),封顶 cap;jitter ±20%。

func CanAutoReplay

func CanAutoReplay(s ReplaySafety) bool

CanAutoReplay 报告该 safety 是否允许自动重投。

func CanManualReplay

func CanManualReplay(s ReplaySafety) bool

CanManualReplay 报告该 safety 是否允许人工重放(三类都允许;ReplayUnsafe 需额外双人确认)。

func CanTransition

func CanTransition(from, to Status) bool

CanTransition 报告 from→to 是否合法。

func IsPermanent

func IsPermanent(err error) bool

IsPermanent 报告 err 链上是否含 PermanentError。

func IsRetryable

func IsRetryable(err error) bool

IsRetryable 报告 err 链上是否含 RetryableError。

func IsTerminal

func IsTerminal(s Status) bool

IsTerminal 报告该状态是否已终结。

func Permanent

func Permanent(err error) error

Permanent 把 err 标记为 POISON(终态 DEAD_LETTER)。handler 对「外键关系不会最终一致」等 明确判断也可用它覆盖第 2 级 1452→Retryable 的默认(§5)。

func Retryable

func Retryable(err error) error

Retryable 把 err 标记为 RETRYABLE(终点由 §6.1 矩阵按 ReplaySafety 决定)。

func SanitizeForLog added in v1.7.4

func SanitizeForLog(s string) string

SanitizeForLog is the log-line redaction entry point (F4-extend). The DLQ cause is scrubbed before it lands in a DB row, but the SAME cause — which can echo the tenant DSN — was otherwise rendered to logs verbatim (the sdk logger has no built-in redaction). Route every cause/err rendered into a reliable- path log line through this. It shares the storage scrubber so the log and storage paths can never diverge below the D11 floor; the two entry points are distinct only so callers express intent and so a future lighter-touch log form can be introduced without touching storage callers.

func SanitizeForStorage added in v1.7.4

func SanitizeForStorage(s string) string

SanitizeForStorage scrubs secrets and truncates to 2KB on a rune boundary (spec §10). The ordering is truncate → regex → truncate:

  • The PRE-truncate bounds the regex input. The DLQ cause is untrusted error text (a GORM/driver message can itself echo attacker bytes); capping it first keeps the regex engine linear even if a future pattern reintroduces backtracking.
  • The POST-truncate re-enforces the 2KB storage ceiling because redaction can GROW the string ([REDACTED] is longer than a short match).

The rune-boundary backoff (C5) is preserved at both truncation sites: a naive s[:2048] splits a multi-byte character in half and produces invalid UTF-8, which the utf8mb4 column then rejects (MySQL error 1366) or silently replaces with U+FFFD. CJK error messages routinely hit this.

This is the canonical root scrubber. The DLQ adapter (adapters/eventbus) and the service-side EventBusDLQAdapter both route quarantine error text through it; the kernel's own MarkFailed/RecordTerminal paths still call the internal store/gormshared.sanitizeMsg (unchanged — additive PR, no delegation wired).

func ShouldDeadLetter

func ShouldDeadLetter(attempt, maxAttempts int) bool

ShouldDeadLetter 报告「已开始的业务执行次数」是否已达上限。

Types

type AggregateGateKey

type AggregateGateKey struct {
	TenantID      int
	AggregateType string
	AggregateID   string
}

AggregateGateKey 是 DB aggregate lease 的身份(§6.2.1)。同 key 串行,不同 key 并行。

func (AggregateGateKey) Empty

func (a AggregateGateKey) Empty() bool

Empty 报告聚合身份是否为空(无聚合的通知类事件跳过 gate)。

type Alerter

type Alerter interface {
	AlertPoison(handlerID HandlerID, eventID string, cause error)
	AlertRecordFailure(handlerID HandlerID, cause error)
	AlertAnomaly(kind string, handlerID HandlerID, detail string)
}

Alerter 把「需人工立刻看」的事件推给服务侧告警通道(§10 P1)。

type ClaimInput

type ClaimInput struct {
	Key      Key
	Meta     Meta
	TenantID int
	// Delivery 是 eventbus.RawMeta 的 kernel 侧投影(避免 import eventbus,从而不把 sarama 带进 kernel)。
	Delivery DeliveryMeta
}

ClaimInput 是 TryClaim 一次取得 Key、业务 Meta、tenant 与 broker RawMeta 的入参(§3.1 v2.7)。

type ClaimToken

type ClaimToken string

ClaimToken 是占位的「票据」(fencing token)。**对调用方是不透明字符串**:禁止解析、拆分或假定其内部结构 (PR-3 adapter 只透传、比较)。MarkSucceeded/MarkFailed/MoveToDeadLetterWithToken 凭它做 WHERE claim_id = ? 校验(§3.1);持令牌者丢失所有权(租约被回收)时命中 0 行 → reliable.ErrConflict(review #17)。

注意:AcquireAggregateGate 返回的 gate token(string)是另一种格式(holder+uuid),不可与 ClaimToken 互换。

func (ClaimToken) String

func (t ClaimToken) String() string

String 返回底层 claim_id,供日志/调试。

type ConsumptionMetrics

type ConsumptionMetrics interface {
	IncDeadLetter(handlerID HandlerID, class ErrorClass)
	IncRecordFailure(handlerID HandlerID)
	IncAnomaly(kind string, handlerID HandlerID)
	IncReplayBlocked(handlerID HandlerID)
	SetPending(status Status, handlerID HandlerID, tenantID int, n int64)
}

ConsumptionMetrics 由服务用自有 prometheus registry 实现(core 只定义接口,J2 不引 prometheus)。

type Decision

type Decision int

Decision 是 TryClaim 的三种结果(§3.1)。

const (
	// Claimed 拿到票据,可以处理。
	Claimed Decision = iota
	// AlreadyProcessing 他人持有且租约未过期:不 ACK,交给 broker 稍后重投(返回 ErrRetryLater)。
	AlreadyProcessing
	// AlreadySettled 已终结(SUCCEEDED/DEAD_LETTER/DISCARDED):直接 ACK。
	AlreadySettled
)

type DeliveryMeta

type DeliveryMeta struct {
	Topic           string
	Partition       int32
	Offset          int64
	BrokerTimestamp time.Time
	PayloadHash     string
	RawKey          []byte
	Headers         []HeaderPair
}

DeliveryMeta 是 RawMeta 的 kernel 侧投影。由消费服务从 eventbus.RawMeta 映射。

type ErrorClass

type ErrorClass string

ErrorClass 是错误分类的结果(§5)。

const (
	// ClassRetryable 瞬态:DB 死锁/锁等待/超时/连接限/关停中/1452 缺父(默认)。
	ClassRetryable ErrorClass = "RETRYABLE"
	// ClassPoison 永不可恢复(消息结构损坏或永不变的业务规则)。终点 DEAD_LETTER(§5 v2.6)。
	ClassPoison ErrorClass = "POISON"
	// ClassUnrecoverable 兜底 + schema 漂移。
	ClassUnrecoverable ErrorClass = "UNRECOVERABLE"
	// ClassConflict 未被第 1 级认领的唯一冲突(1062/23505 未核实为幂等命中)。终点 DEAD_LETTER。
	ClassConflict ErrorClass = "CONFLICT"
	// ClassSkip 仅由 handler 第 1 级显式声明(ErrSkip);第 2 级不兜底判 Skip。终点按成功终结。
	ClassSkip ErrorClass = "SKIP"
)

func Classify

func Classify(err error, driver ErrorClassifier) ErrorClass

Classify 是两级分类的纯函数(§5):

  1. 领域显式声明(权威):PermanentError→Poison;RetryableError→Retryable;ErrSkip→Skip。
  2. 基础设施错误码(精确,保守默认):driver 识别 + context 超时/取消 + net 超时。
  3. 兜底 Unrecoverable(v2.5:未知不再当 Retryable)。

type ErrorClassifier

type ErrorClassifier interface {
	// ClassifyDriver 只做保守默认;返回 (class, true) 表示命中已知驱动错误码,(0, false) 表示不认识。
	ClassifyDriver(err error) (ErrorClass, bool)
	// IsDuplicateKey 报告 err 是否是该驱动的唯一冲突错误(TryClaim dup 检测用,D3)。
	IsDuplicateKey(err error) bool
	// ErrorCode 提取驱动原生错误码字符串(如 MySQL "1213"、PostgreSQL "23505"),
	// 用于填充 error_code 列(有界、可聚合的稳定代码,§5)。返回 (code, true) 表示已提取,
	// ("", false) 表示不认识的错误——调用方回落 Classify() 的 class 名作为 code。
	ErrorCode(err error) (string, bool)
}

ErrorClassifier 是 driver classifier 的统一接口(§5 第 2 级)。 实现放 store/mysql(识别 *mysql.MySQLError.Number)与 store/postgres(识别 *pgconn.PgError.Code)。 kernel 不 import 任何数据库驱动——这就是为什么 classifier 是注入而非硬编码。 dup 检测(TryClaim 的 Create 竞态)也复用此接口,避免字符串匹配(D3)。

type HandlerID

type HandlerID string

HandlerID 是持久协议标识,不随 Go 类型/函数重命名而变化(§3.1)。

type HeaderPair

type HeaderPair struct {
	Key   string
	Value []byte
}

HeaderPair 是有序、允许重复 key 的 header(镜像 eventbus.MessageHeader,但不依赖该包)。

type Key

type Key struct {
	EventID string
	Handler HandlerID
	ItemKey string
}

Key 是 event_consumption 的全局身份(§3.1):

  • EventID 恒为 Envelope.EventID = 生产端 outbox 行 id(M5);
  • Handler 稳定 HandlerID;
  • ItemKey 单事件恒为空串;批量 item 填稳定业务身份(禁止用数组下标,M11 v2.6)。

func (Key) Validate

func (k Key) Validate() error

Validate 校验 Key 三要素。

type Meta

type Meta struct {
	EventType     string
	AggregateType string
	AggregateID   string
	CausalSeq     *int64
}

Meta 描述性元数据(§3.1)。AggregateType/AggregateID 用于 §6.2.1 跨 topic/跨服务聚合分组; CausalSeq 是事件自带的领域版本号(没有则 nil,跨 topic 排序退化为同 topic 内可靠,v2.7)。

type MetaProvider

type MetaProvider interface{ Meta() Meta }

MetaProvider 是默认提取方式。无法修改既有事件 DTO 时,装饰器接受显式 MetaFunc(§3.1)。

type NoOpAlerter

type NoOpAlerter struct{}

NoOpAlerter 零实现(测试用)。

func (NoOpAlerter) AlertAnomaly

func (NoOpAlerter) AlertAnomaly(string, HandlerID, string)

func (NoOpAlerter) AlertPoison

func (NoOpAlerter) AlertPoison(HandlerID, string, error)

func (NoOpAlerter) AlertRecordFailure

func (NoOpAlerter) AlertRecordFailure(HandlerID, error)

type NoOpMetrics

type NoOpMetrics struct{}

NoOpMetrics 是零实现,供未接入指标的服务/测试使用(不得作为生产默认)。

func (NoOpMetrics) IncAnomaly

func (NoOpMetrics) IncAnomaly(string, HandlerID)

func (NoOpMetrics) IncDeadLetter

func (NoOpMetrics) IncDeadLetter(HandlerID, ErrorClass)

func (NoOpMetrics) IncRecordFailure

func (NoOpMetrics) IncRecordFailure(HandlerID)

func (NoOpMetrics) IncReplayBlocked

func (NoOpMetrics) IncReplayBlocked(HandlerID)

func (NoOpMetrics) SetPending

func (NoOpMetrics) SetPending(Status, HandlerID, int, int64)

type PermanentError

type PermanentError struct{ Cause error }

PermanentError 包装一个「永不可恢复」的根因:消息结构损坏或触发了永不会变的业务规则。 两者终点相同(DEAD_LETTER),排查思路不同——以 error_message 区分(§5 v2.6)。

func (*PermanentError) Error

func (e *PermanentError) Error() string

func (*PermanentError) Unwrap

func (e *PermanentError) Unwrap() error

type ReplaySafety

type ReplaySafety int

ReplaySafety 按 handler 声明(§6.1)。未实现 ReplayableHandler 的 handler 默认 ReplayUnsafe。

const (
	// ReplayUnsafe 默认值:有进程外副作用。可重试失败直接进 DEAD_LETTER(§6.1)。
	ReplayUnsafe ReplaySafety = iota
	// ReplayNeedsTxClaim 会写新行,需事务化占位。
	ReplayNeedsTxClaim
	// ReplayIdempotent 纯 upsert / delete-by-id / set-state,可安全自动重放。
	ReplayIdempotent
)

type ReplayableHandler

type ReplayableHandler interface {
	Handle(ctx context.Context, envelopeBytes []byte, delivery DeliveryMeta) error
	HandlerID() HandlerID
	ReplaySafety() ReplaySafety
	RequiresAggregateGate() bool
}

ReplayableHandler 是可靠消费装饰器对 handler 的能力要求(§6.1)。

**签名偏离 spec §6.1(本轮评审 C3)**:spec §6.1 把 ReplayableHandler 定义为嵌入 `eventbus.EnvelopeDeliveryHandler`(收 `*Envelope` + `RawMeta`)。本计划改为 `Handle(ctx, []byte, DeliveryMeta)` ——raw envelope bytes + kernel 侧 DeliveryMeta 投影。这是刻意的 J2 决策(kernel 根包不得 import eventbus/sarama)。PR-3 的可靠消费 decorator 负责把 `env.ToBytes()` 的 bytes 解码回 `*Envelope` 再调 handler;spec §6.1 的 `EnvelopeDeliveryHandler` 嵌入由 decorator 层实现,不在 kernel 接口体现。

type RetryableError

type RetryableError struct{ Cause error }

RetryableError 包装一个「可重试」的根因(瞬态 DB 超时/死锁/锁等待等)。

func (*RetryableError) Error

func (e *RetryableError) Error() string

func (*RetryableError) Unwrap

func (e *RetryableError) Unwrap() error

type Status

type Status string

Status 是 event_consumption.status 的五态枚举(§2.1/§3)。

const (
	StatusProcessing     Status = "PROCESSING"
	StatusSucceeded      Status = "SUCCEEDED"
	StatusRetryScheduled Status = "RETRY_SCHEDULED"
	StatusDeadLetter     Status = "DEAD_LETTER"
	StatusDiscarded      Status = "DISCARDED"
)

type TerminalOutcome

type TerminalOutcome struct{ DeadLetter bool }

TerminalOutcome 是 MarkFailed 按 §6.1 矩阵推导的终点。

func OutcomeFor

func OutcomeFor(class ErrorClass, safety ReplaySafety) TerminalOutcome

OutcomeFor 实现 §6.1 的 ErrorClass × ReplaySafety 矩阵(纯函数,双方言 store 共用)。

Directories

Path Synopsis
adapters
eventbus
Package eventbusdlq is the core DLQ adapter bridging the jxt-core partition- pipeline DLQSender contract to the reliable store (spec §7 "handler claim 前 / transport 失败" path).
Package eventbusdlq is the core DLQ adapter bridging the jxt-core partition- pipeline DLQSender contract to the reliable store (spec §7 "handler claim 前 / transport 失败" path).
Package gate 提供 aggregate-gate 的获取/释放机制(§6.2.1)。
Package gate 提供 aggregate-gate 的获取/释放机制(§6.2.1)。
Package lease 周期**观测**过期 PROCESSING 租约(§3.2)。
Package lease 周期**观测**过期 PROCESSING 租约(§3.2)。
Package opsvc 是 reliable 内核的 §10 ops 服务层(PR-2 Task B2)。
Package opsvc 是 reliable 内核的 §10 ops 服务层(PR-2 Task B2)。
Package replay 实现 eligible-head 重放调度器(§6.2/§6.2.1)。
Package replay 实现 eligible-head 重放调度器(§6.2/§6.2.1)。
Package store 定义 reliable 持久化的方言中立抽象:Row 领域 model 与 Store 接口。
Package store 定义 reliable 持久化的方言中立抽象:Row 领域 model 与 Store 接口。
gormshared
Package gormshared 是 reliable Store 的共享 GORM 实现(D17)。
Package gormshared 是 reliable Store 的共享 GORM 实现(D17)。
mysql
Package mysql 是 reliable Store 的 MySQL 薄方言包(D17)。
Package mysql 是 reliable Store 的 MySQL 薄方言包(D17)。
postgres
Package postgres 是 reliable Store 的 PostgreSQL 薄方言包(D17)。
Package postgres 是 reliable Store 的 PostgreSQL 薄方言包(D17)。
repotest
Package repotest 是 reliable Store 的双方言共享 conformance suite(项目首创,准入 ⑭)。
Package repotest 是 reliable Store 的双方言共享 conformance suite(项目首创,准入 ⑭)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL