idempotency

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package idempotency 提供幂等执行原语:同一 key 的重复请求只执行一次, 后续请求(含并发)复用首次结果,直到该 key 过期。

解决的问题:网络重试 / 客户端重发 / 消息重投会让同一逻辑操作执行多次—— 抽卡重复扣钱、充值重复到账、发奖重复发放。给每次操作分配一个幂等 key (订单号 / 请求 ID / txID),用本原语包住执行体,重复 key 不再重复执行。

两条语义合一:

  • 去重(dedup):key 已有完成结果 → 直接返回缓存的结果,不再执行 fn;
  • 并发合并(singleflight):同一 key 多个请求同时到达 → 只有一个执行 fn, 其余阻塞等待同一结果,避免"缓存击穿式"重复执行。

结果按 TTL 过期(默认 10 分钟),到点后同 key 可重新执行。fn 返回 error 时 默认不缓存(允许重试);可用 WithCacheErrors 改为连错误一起缓存(适合 "确定性失败无需重试"的场景)。

泛型 T 为结果类型。并发安全。零值不可用,用 New 构造;Stop 后清扫 goroutine 退出。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option func(*config)

Option 配置 Store。

func WithCacheErrors

func WithCacheErrors(cache bool) Option

WithCacheErrors 设置是否缓存失败结果(默认 false)。 false:fn 返回 error 不缓存,同 key 下次请求会重新执行(适合瞬时错误可重试); true:错误也按 TTL 缓存,同 key 直接返回该错误(适合确定性失败,避免无谓重试)。 注意:store 模式只缓存**成功**结果(错误不跨进程序列化),此选项仅内存模式生效。

func WithGCInterval

func WithGCInterval(d time.Duration) Option

WithGCInterval 设置过期清扫间隔(默认 1 分钟,仅内存模式)。

func WithOnStoreError

func WithOnStoreError(fn func(op, key string, err error)) Option

WithOnStoreError 设置 store 出错回调。默认静默 + 降级为直接执行 fn。

func WithStore

func WithStore(s kvstore.Store) Option

WithStore 让幂等结果走外部共享存储(如 Redis),使去重跨实例生效。 结果用 JSON 序列化后存入 store(故 T 须可被 encoding/json 编解码)。

语义差异(务必知悉):内存模式提供"去重 + 并发单飞(阻塞等待首次结果)"; store 模式只提供**去重 + 复用已完成结果**——用 SetNX 抢占执行权,抢到的执行 fn 并写回结果,没抢到的**不阻塞等待**,而是读已有结果(若尚未写完则自己执行一次)。 即:跨实例的并发同 key 可能各自执行一次,幂等性由"结果最终以 key 唯一存储 + 复用" 保证,而非"全局只执行一次"。要严格单飞请在 fn 内配合分布式锁,或接受此 at-least-once 语义(这正是幂等键要求业务操作本身可安全重试的原因)。 只缓存成功结果;fn 返回 error 时不写 store,允许重试。

func WithTTL

func WithTTL(d time.Duration) Option

WithTTL 设置结果缓存时长(默认 10 分钟)。到期后同 key 可重新执行。

type Store

type Store[T any] struct {
	// contains filtered or unexported fields
}

Store 幂等结果存储。按 key 维护"执行中/已完成"记录。 零值不可用,用 New 构造。并发安全。

func New

func New[T any](opts ...Option) *Store[T]

New 创建幂等 Store 并启动清扫 goroutine。

func (*Store[T]) Do

func (s *Store[T]) Do(key string, fn func() (T, error)) (result T, err error, shared bool)

Do 以 key 为幂等键执行 fn:

  • key 首次出现:执行 fn,缓存结果(按配置决定是否缓存 error),返回 (result, err, false);
  • key 执行中(并发):阻塞等待首次执行完成,返回其结果 + shared=true;
  • key 已完成且未过期:直接返回缓存结果 + shared=true,不执行 fn。

shared 表示本次结果是否复用自其他请求(true=未真正执行 fn)。 fn 内 panic 不被捕获——调用方若需防护请在 fn 内自行 recover;panic 会 导致等待同 key 的其他请求也观察到该记录被清理(可重试)。

func (*Store[T]) Forget

func (s *Store[T]) Forget(key string)

Forget 立即删除 key 的记录(不论执行中还是已完成)。 执行中的记录被 Forget 后,正在执行的 fn 结果不再被后续请求复用。

func (*Store[T]) Get

func (s *Store[T]) Get(key string) (T, bool)

Get 查询 key 的已完成结果(不触发执行)。 返回 (result, ok):ok=false 表示无记录、执行中、或已过期。

func (*Store[T]) Len

func (s *Store[T]) Len() int

Len 返回当前记录数(含执行中与未清扫的过期记录)。

func (*Store[T]) Stop

func (s *Store[T]) Stop()

Stop 停止清扫 goroutine。幂等。

Jump to

Keyboard shortcuts

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