feat: add generic queue and subscription retirement

This commit is contained in:
2026-09-27 14:39:49 +08:00
parent d54bdd9847
commit c9e877c3f7
53 changed files with 1013 additions and 88 deletions

View File

@@ -72,19 +72,23 @@ type CreateInput struct {
}
type Subscription struct {
ID core.ID `json:"id"`
QueueID core.ID `json:"queue_id"`
CredentialID core.ID `json:"credential_id"`
Name string `json:"name"`
Revision uint64 `json:"revision,string"`
Enabled bool `json:"enabled"`
Types []string `json:"types"`
CursorSequence uint64 `json:"cursor_sequence,string"`
PendingCount uint32 `json:"pending_count"`
MaxPending uint32 `json:"max_pending"`
MaxAttempts uint32 `json:"max_attempts"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// RetiredAt 永久退役时间;退役后不可重新启用且不占活动订阅名额。
RetiredAt *time.Time `json:"retired_at,omitempty"`
// RetiredSequence 退役时的结果流截止水位,十进制字符串;之后的结果不再订阅。
RetiredSequence *uint64 `json:"retired_sequence,string,omitempty"`
ID core.ID `json:"id"`
QueueID core.ID `json:"queue_id"`
CredentialID core.ID `json:"credential_id"`
Name string `json:"name"`
Revision uint64 `json:"revision,string"`
Enabled bool `json:"enabled"`
Types []string `json:"types"`
CursorSequence uint64 `json:"cursor_sequence,string"`
PendingCount uint32 `json:"pending_count"`
MaxPending uint32 `json:"max_pending"`
MaxAttempts uint32 `json:"max_attempts"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type SubscriptionResult struct {
@@ -92,6 +96,12 @@ type SubscriptionResult struct {
Deduplicated bool `json:"deduplicated"`
}
// RetireInput 永久停止未来订阅;只在所有历史结果已确认时成功。
type RetireInput struct {
ExpectedRevision uint64 `json:"expected_revision,string"` // ExpectedRevision 当前配置版本。
Reason string `json:"reason"` // Reason 退役原因,写入不可变审计。
}
type ControlInput struct {
ExpectedRevision uint64 `json:"expected_revision,string"`
Enabled bool `json:"enabled"`
@@ -160,6 +170,7 @@ type Store interface {
GetDeliveryDetail(context.Context, auth.Token, core.ID, core.ID) (DeliveryDetail, error)
CreateSubscription(context.Context, auth.Token, string, CreateInput) (SubscriptionResult, error)
GetSubscription(context.Context, auth.Token, core.ID) (Subscription, error)
RetireSubscription(context.Context, auth.Token, core.ID, string, RetireInput) (SubscriptionResult, error)
ControlSubscription(context.Context, auth.Token, core.ID, string, ControlInput) (SubscriptionResult, error)
GetDelivery(context.Context, auth.Token, core.ID, core.ID) (Delivery, error)
ClaimDelivery(context.Context, auth.Token, core.ID, ClaimInput) (*Grant, error)
@@ -233,6 +244,15 @@ func (s *Service) Control(ctx context.Context, t auth.Token, id core.ID, key str
}
return s.store.ControlSubscription(ctx, t, id, key, in)
}
func (s *Service) Retire(ctx context.Context, t auth.Token, id core.ID, key string, in RetireInput) (SubscriptionResult, error) {
if err := keyValid(key); err != nil {
return SubscriptionResult{}, err
}
if id.Validate() != nil || !reasonValid(in.ExpectedRevision, in.Reason) {
return SubscriptionResult{}, fault.Invalid("subscription_id, revision and reason are required")
}
return s.store.RetireSubscription(ctx, t, id, key, in)
}
func (s *Service) Delivery(ctx context.Context, t auth.Token, id, delivery core.ID) (Delivery, error) {
if id.Validate() != nil || delivery.Validate() != nil {
return Delivery{}, fault.Invalid("invalid subscription_id or delivery_id")

View File

@@ -1,6 +1,8 @@
package model
import (
"time"
core "git.yuntuo.hk/k2safe/orbit/model"
)
@@ -9,6 +11,10 @@ import (
type Subscription struct {
// Record 包含命名空间、记录标识、并发版本和 UTC 时间。
core.Record
// RetiredAt 永久退役的UTC时间;空表示未退役,暂停仍占活动名额。
RetiredAt *time.Time `db:"retired_at"`
// RetiredSequence 退役时锁定的结果流提交水位;空表示未退役,后续结果不再订阅。
RetiredSequence *uint64 `db:"retired_sequence"`
// Name 可读名称,唯一性范围由对应数据表约束。
Name string `db:"name"`
// CredentialID 关联的命名空间凭据标识。