chore: 初始开源发布
iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。 - Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件) - 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、 库名 xiujie_admin → iotlcm、哨兵名占位 - 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异, 不破坏「校验 MSISDN 格式」等测试断言) - 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像 digest 记录 - Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23), 移除写死的 git 凭据 - README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 已知:3 个测试包在源仓同一提交上同样失败(client/upstream、 service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package contract
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RunReader is the HTTP lookup boundary. Fall back to archive only when the
|
||||
// live reader returns ErrNotFound, not on transient live storage failures.
|
||||
type RunReader interface {
|
||||
GetRun(ctx context.Context, runID string) (RunView, error)
|
||||
}
|
||||
|
||||
type RunListFilter struct {
|
||||
SyncKind SyncKind
|
||||
Limit int
|
||||
}
|
||||
|
||||
type RunList struct {
|
||||
Items []RunView `json:"items"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type RunLister interface {
|
||||
ListRuns(ctx context.Context, filter RunListFilter) (RunList, error)
|
||||
}
|
||||
|
||||
type LiveRunReader interface {
|
||||
GetRunMeta(ctx context.Context, runID string) (RunMeta, error)
|
||||
}
|
||||
|
||||
type ArchivedRunReader interface {
|
||||
GetArchivedRun(ctx context.Context, runID string) (RunArchive, error)
|
||||
}
|
||||
|
||||
type RoundStatsReader interface {
|
||||
GetRoundStats(ctx context.Context, runID string) ([]RoundStats, error)
|
||||
}
|
||||
|
||||
// FailurePager returns live details only. An archived summary without live
|
||||
// details returns ErrFailureDetailsUnavailable, never a misleading empty page.
|
||||
// Items must serialize as [] on empty pages; NextCursor alone signals EOF.
|
||||
type FailurePager interface {
|
||||
ListFailures(ctx context.Context, runID string, page PageRequest) (FailurePage, error)
|
||||
}
|
||||
|
||||
type ReplayRequester interface {
|
||||
RequestReplay(ctx context.Context, request ReplayRequest) (ReplayAccepted, error)
|
||||
}
|
||||
|
||||
type ActionReader interface {
|
||||
GetReplayAction(ctx context.Context, actionID string) (ReplayAction, error)
|
||||
}
|
||||
|
||||
// ArchiveWorkDiscovery returns pending/retryable work from a durable index.
|
||||
// Terminal run registration must not depend on an in-memory notification.
|
||||
type ArchiveWorkDiscovery interface {
|
||||
ListArchiveWork(ctx context.Context, page PageRequest) (ArchiveWorkPage, error)
|
||||
}
|
||||
|
||||
// RegisterArchiveWork idempotently schedules a terminal run/version. Since the
|
||||
// index can occupy another Redis slot, callers must reconcile registration
|
||||
// after a crash between completing the run and updating the index.
|
||||
type ArchiveWorkRegistrar interface {
|
||||
RegisterArchiveWork(ctx context.Context, work ArchiveWork) error
|
||||
}
|
||||
|
||||
type ArchiveWorkLoader interface {
|
||||
LoadArchiveWork(ctx context.Context, work ArchiveWork) (RunArchive, error)
|
||||
}
|
||||
|
||||
// ArchiveRegistrar conditionally records a durable publication against the
|
||||
// work version. Repeating the same receipt is a no-op; a different version or
|
||||
// conflicting receipt returns ErrConflict. Failure retains discoverable work.
|
||||
type ArchiveRegistrar interface {
|
||||
RegisterArchive(ctx context.Context, work ArchiveWork, receipt ArchiveReceipt) error
|
||||
RegisterArchiveFailure(ctx context.Context, work ArchiveWork, message string) error
|
||||
}
|
||||
|
||||
// ReplayActionCreator atomically reserves (sourceRunId, idempotencyKey) with
|
||||
// its fingerprint and action. Same key/request returns the original action
|
||||
// regardless of candidate IDs; different request returns ErrConflict. The
|
||||
// candidate starts accepted and fixes the target ID before any target writes.
|
||||
type ReplayActionCreator interface {
|
||||
GetOrCreateReplayAction(ctx context.Context, request ReplayRequest, candidate ReplayAction) (ReplayActionCreation, error)
|
||||
}
|
||||
|
||||
// Discovery includes accepted, processing, cleanup_pending and receipt_pending
|
||||
// actions after crashes. Completed/needs_attention actions require no automatic
|
||||
// work. Lease acquisition, not listing, chooses an exclusive owner.
|
||||
type ReplayWorkDiscovery interface {
|
||||
ListReplayWork(ctx context.Context, page PageRequest) (ReplayActionPage, error)
|
||||
}
|
||||
|
||||
type ReplayLeaseStore interface {
|
||||
AcquireReplayLease(ctx context.Context, actionID, owner string, ttl time.Duration) (ReplayLease, error)
|
||||
RenewReplayLease(ctx context.Context, lease ReplayLease, ttl time.Duration) (ReplayLease, error)
|
||||
ReleaseReplayLease(ctx context.Context, lease ReplayLease) error
|
||||
}
|
||||
|
||||
// All updates are fenced and compare ExpectedRevision. Expired/replaced leases
|
||||
// return ErrLeaseLost; revision conflicts return ErrConflict. Implementations
|
||||
// reject progress regression and preserve immutable action identity.
|
||||
type ReplayProgressStore interface {
|
||||
UpdateReplayAction(ctx context.Context, update ReplayActionUpdate) (ReplayAction, error)
|
||||
}
|
||||
|
||||
// The implementation must preserve scan continuity while source entries are
|
||||
// cleaned. This can use a durable selection/snapshot; deleting records must not
|
||||
// make a later cursor skip an unprocessed failure. Tokens are bound to actionID.
|
||||
type ReplayFailurePager interface {
|
||||
ListReplayFailures(ctx context.Context, actionID string, page PageRequest) (FailureRecordPage, error)
|
||||
}
|
||||
|
||||
// TargetRunCreator is idempotent: retrying the same target preserves counters;
|
||||
// incompatible source/operation/month/cycle returns ErrConflict. Replay targets
|
||||
// are distinct runs and must not mutate the source's historical FailedCount.
|
||||
type TargetRunCreator interface {
|
||||
EnsureTargetRun(ctx context.Context, meta RunMeta) (RunMeta, error)
|
||||
}
|
||||
|
||||
// TargetEnqueueResult distinguishes a durable duplicate from a rejected write.
|
||||
// A zero NewlyEnqueued count alone is never sufficient evidence for cleanup.
|
||||
// Every input TaskID must be durably present on nil error; otherwise return an
|
||||
// error and let the caller safely retry the identical batch. Both counts are
|
||||
// unique task counts, and their sum equals the number of distinct input IDs.
|
||||
type TargetEnqueueResult struct {
|
||||
NewlyEnqueued int64
|
||||
AlreadyPresent int64
|
||||
}
|
||||
|
||||
type TargetRunEnqueuer interface {
|
||||
// Tasks must match the target run and channel and use stable logical IDs.
|
||||
EnqueueTargetBatch(ctx context.Context, lease ReplayLease, runID, chanKey string, tasks []Task, nextExecuteAt time.Time) (TargetEnqueueResult, error)
|
||||
MarkTargetProducerDone(ctx context.Context, lease ReplayLease, runID string) error
|
||||
}
|
||||
|
||||
// Cleanup is idempotent and conditional on the lease, exact source versions,
|
||||
// and durable evidence of target acceptance. It changes pending failures only,
|
||||
// never historical failedCount. Conflicts must remain visible for attention.
|
||||
// Source keys and action/target keys may occupy different Redis Cluster slots;
|
||||
// implementations need recoverable receipts, not cross-slot Lua assumptions.
|
||||
type ReplayFailureCleaner interface {
|
||||
CleanupReplayFailures(ctx context.Context, cleanup ReplayCleanup) (ReplayCleanupResult, error)
|
||||
}
|
||||
Reference in New Issue
Block a user