Help us shape the future of Go! Take the 2026 Go Developer Survey

cow

package module
v1.0.5 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

README

cow

单协程聚合根 Undo Log 写代理:业务失败时倒序执行逆操作回滚;成功路径不拷贝聚合根数据,仅清空 TxContext 日志。

模块路径:github.com/huangyuCN/cow

解决的问题

  • 补偿脆弱:请求处理链变长后,手写回滚/补偿易漏,难测试。
  • DeepCopy 成本高:每次请求对整棵聚合根做全量深拷贝,在大 map/slice 场景下 CPU、分配与 GC 压力大。

前提

宿主对同一聚合根提供 单 goroutine 串行写(或等价保证)。TxContext 不加锁,不可跨 goroutine 共享。

能力边界

  • 不提供 TxContext 并发安全。
  • 不提供运行期裸写检测(仅静态分析器 undocheck / cowbarewrite)。
  • undoproxy-gen 仅支持 同包 类型图;容器元素不支持 interface{}、channel、func。
  • 不捆绑具体 Actor / HTTP / 消息框架;文档提供接入模式,由宿主嵌入。

快速开始

# 引入模块(或 go.work replace 本地路径)
go get github.com/huangyuCN/cow@latest

# 1. 在聚合根上标记并生成代理
go install ./cmd/undoproxy-gen
# types.go: // +cow:undoproxy-gen=true
go generate ./...

# 2. 静态禁止裸写
go install ./cmd/undocheck
go vet -vettool=$(go env GOPATH)/bin/undocheck ./...

单次请求作用域示例(失败回滚):

ctx := txPool.Get().(*TxContext)
ctx.Reset()
defer func() {
    if err != nil {
        ctx.Rollback()
    }
    txPool.Put(ctx)
}()
player.PutAssets(ctx, "gold", 500)
// ...

成功提交:业务无错时 ctx.Reset() 清空日志即可(见 docs/guide/tx-context.md)。

完整接入示例

独立 module 演示(自有聚合根、go generate、Rollback/Commit):examples/gamestore/README.md。

文档

文档 说明
docs/README.md 文档总索引(含贡献者阅读顺序)
docs/guide/ 集成方功能手册(推荐从此入手)
docs/toolchain/ 维护者工具链说明
cmd/ 各子命令 README
CONTRIBUTING.md 贡献流程与约定
docs/superpowers/ 维护者设计 spec / plan / benchmark 档案

License

Apache License 2.0,见 LICENSE。

Documentation

Overview

Package cow 提供单协程聚合根 Undo Log 写代理。

裸写静态检查(需先安装分析器):

go install ./cmd/undocheck
go vet -vettool=$(go env GOPATH)/bin/undocheck ./...

历史裸写批量改写(默认 dry-run,确认后加 -w):

go install ./cmd/undorewrite
undorewrite ./yourpkg/...

完整说明见仓库 README.md 与 docs/guide/。

+k8s:deepcopy-gen=package +cow:undoproxy-gen=package +groupName=cow.huanghaiyu.cn

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Hero

type Hero struct {
	HeroId int32            `protobuf:"varint,1,opt,name=hero_id" json:"hero_id,omitempty" bson:"hero_id"`
	Level  int32            `protobuf:"varint,2,opt,name=level" json:"level,omitempty" bson:"level"`
	Skills map[int32]*Skill `json:"skills,omitempty" bson:"skills"`
}

Hero 模拟英雄子结构。

+k8s:deepcopy-gen=true

func (*Hero) CloneForWrite

func (h *Hero) CloneForWrite() *Hero

CloneForWrite 返回 Hero 的可写浅拷贝。

func (*Hero) DeepCopy

func (in *Hero) DeepCopy() *Hero

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hero.

func (*Hero) DeepCopyInto

func (in *Hero) DeepCopyInto(out *Hero)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*Hero) GetSkillsForWrite added in v1.0.4

func (h *Hero) GetSkillsForWrite(ctx *TxContext, k1 int32) *Skill

func (*Hero) PutHeroId

func (h *Hero) PutHeroId(ctx *TxContext, val int32)

func (*Hero) PutLevel

func (h *Hero) PutLevel(ctx *TxContext, val int32)

func (*Hero) PutSkills

func (h *Hero) PutSkills(ctx *TxContext, k1 int32, val *Skill)

func (*Hero) RemoveSkills added in v1.0.1

func (h *Hero) RemoveSkills(ctx *TxContext, k1 int32)

type Item

type Item struct {
	Id    int64  `protobuf:"varint,1,opt,name=id" json:"id,omitempty" bson:"_id"`
	Name  string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty" bson:"name"`
	Extra string `json:"extra,omitempty" bson:"extra"`
}

Item 模拟背包条目(标签兼容 PB/BSON;运行路径不依赖序列化)。

+k8s:deepcopy-gen=true

func (*Item) CloneForWrite

func (i *Item) CloneForWrite() *Item

CloneForWrite 返回 Item 的可写浅拷贝。

func (*Item) DeepCopy

func (in *Item) DeepCopy() *Item

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Item.

func (*Item) DeepCopyInto

func (in *Item) DeepCopyInto(out *Item)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*Item) PutExtra

func (i *Item) PutExtra(ctx *TxContext, val string)

func (*Item) PutId

func (i *Item) PutId(ctx *TxContext, val int64)

func (*Item) PutName

func (i *Item) PutName(ctx *TxContext, val string)

type Mail

type Mail struct {
	Id      uint64 `json:"id,omitempty" bson:"id"`
	Subject string `json:"subject,omitempty" bson:"subject"`
	Body    string `json:"body,omitempty" bson:"body"`
}

Mail 邮件(Body 用于拉高 mega 夹具体积)。

+k8s:deepcopy-gen=true

func (*Mail) CloneForWrite

func (m *Mail) CloneForWrite() *Mail

CloneForWrite 返回 Mail 的可写浅拷贝。

func (*Mail) DeepCopy

func (in *Mail) DeepCopy() *Mail

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mail.

func (*Mail) DeepCopyInto

func (in *Mail) DeepCopyInto(out *Mail)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*Mail) PutBody

func (m *Mail) PutBody(ctx *TxContext, val string)

func (*Mail) PutId

func (m *Mail) PutId(ctx *TxContext, val uint64)

func (*Mail) PutSubject

func (m *Mail) PutSubject(ctx *TxContext, val string)

type Player

type Player struct {
	Uid       int64                      `protobuf:"varint,1,opt,name=uid" json:"uid,omitempty" bson:"_id"`
	Level     int32                      `json:"level,omitempty" bson:"level"`
	Assets    map[string]int64           `protobuf:"bytes,2,rep,name=assets" json:"assets,omitempty" bson:"assets"`
	Items     []*Item                    `protobuf:"bytes,3,rep,name=items" json:"items,omitempty" bson:"items"`
	MainHero  *Hero                      `protobuf:"bytes,4,opt,name=hero" json:"hero,omitempty" bson:"hero"`
	Heros     map[int32]*Hero            `json:"heros,omitempty" bson:"heros"`
	Bags      map[int32][]*Item          `json:"bags,omitempty" bson:"bags"`
	Stats     map[int32]map[string]int64 `json:"stats,omitempty" bson:"stats"`
	Cooldowns map[int32][]int32          `json:"cooldowns,omitempty" bson:"cooldowns"`
	Mails     map[uint64]*Mail           `json:"mails,omitempty" bson:"mails"`
	Quests    map[int32]*Quest           `json:"quests,omitempty" bson:"quests"`
}

Player 模拟聚合根(lite + mega 双档构造)。

+k8s:deepcopy-gen=true +cow:undoproxy-gen=true

Example (SparseWrite)

ExamplePlayer_sparseWrite 演示通过生成代理在 TxContext 下稀疏写。

player := newBenchPlayer()
ctx := txPool.Get().(*TxContext)
ctx.Reset()
defer txPool.Put(ctx)

player.PutAssets(ctx, "gold", 500)
fmt.Println(player.Assets["gold"])
Output:
500

func (*Player) AppendBagsAt

func (p *Player) AppendBagsAt(ctx *TxContext, k1 int32, elem *Item)

func (*Player) AppendCooldownsAt

func (p *Player) AppendCooldownsAt(ctx *TxContext, k1 int32, elem int32)

func (*Player) AppendItems

func (p *Player) AppendItems(ctx *TxContext, elem *Item)

func (*Player) CloneForWrite

func (p *Player) CloneForWrite() *Player

CloneForWrite 返回 Player 的可写浅拷贝。

func (*Player) DeepCopy

func (in *Player) DeepCopy() *Player

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Player.

func (*Player) DeepCopyInto

func (in *Player) DeepCopyInto(out *Player)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*Player) GetHerosForWrite added in v1.0.4

func (p *Player) GetHerosForWrite(ctx *TxContext, k1 int32) *Hero

func (*Player) GetItemAtForWrite

func (p *Player) GetItemAtForWrite(ctx *TxContext, k1 int32, i int) *Item

func (*Player) GetMailsForWrite added in v1.0.4

func (p *Player) GetMailsForWrite(ctx *TxContext, k1 uint64) *Mail

func (*Player) GetMainHeroForWrite

func (p *Player) GetMainHeroForWrite(ctx *TxContext) *Hero

func (*Player) GetQuestsForWrite added in v1.0.4

func (p *Player) GetQuestsForWrite(ctx *TxContext, k1 int32) *Quest

func (*Player) GetStatsMapForWrite

func (p *Player) GetStatsMapForWrite(ctx *TxContext, k1 int32) map[string]int64

func (*Player) PutAssets

func (p *Player) PutAssets(ctx *TxContext, k1 string, val int64)

func (*Player) PutBags

func (p *Player) PutBags(ctx *TxContext, k1 int32, val []*Item)

func (*Player) PutCooldowns

func (p *Player) PutCooldowns(ctx *TxContext, k1 int32, val []int32)

func (*Player) PutHeros

func (p *Player) PutHeros(ctx *TxContext, k1 int32, val *Hero)

func (*Player) PutLevel

func (p *Player) PutLevel(ctx *TxContext, val int32)

func (*Player) PutMails

func (p *Player) PutMails(ctx *TxContext, k1 uint64, val *Mail)

func (*Player) PutQuests

func (p *Player) PutQuests(ctx *TxContext, k1 int32, val *Quest)

func (*Player) PutStats

func (p *Player) PutStats(ctx *TxContext, k1 int32, k2 string, val int64)

func (*Player) PutUid

func (p *Player) PutUid(ctx *TxContext, val int64)

func (*Player) RemoveAssets added in v1.0.1

func (p *Player) RemoveAssets(ctx *TxContext, k1 string)

func (*Player) RemoveBagsAt

func (p *Player) RemoveBagsAt(ctx *TxContext, k1 int32, i int)

func (*Player) RemoveCooldownsAt

func (p *Player) RemoveCooldownsAt(ctx *TxContext, k1 int32, i int)

func (*Player) RemoveHeros added in v1.0.1

func (p *Player) RemoveHeros(ctx *TxContext, k1 int32)

func (*Player) RemoveItemsAt

func (p *Player) RemoveItemsAt(ctx *TxContext, i int)

func (*Player) RemoveMails added in v1.0.1

func (p *Player) RemoveMails(ctx *TxContext, k1 uint64)

func (*Player) RemoveQuests added in v1.0.1

func (p *Player) RemoveQuests(ctx *TxContext, k1 int32)

func (*Player) RemoveStats added in v1.0.1

func (p *Player) RemoveStats(ctx *TxContext, k1 int32, k2 string)

func (*Player) SetBagsAt

func (p *Player) SetBagsAt(ctx *TxContext, k1 int32, i int, elem *Item)

func (*Player) SetCooldownsAt

func (p *Player) SetCooldownsAt(ctx *TxContext, k1 int32, i int, elem int32)

func (*Player) SetItemsAt

func (p *Player) SetItemsAt(ctx *TxContext, i int, elem *Item)

func (*Player) SetMainHero added in v1.0.1

func (p *Player) SetMainHero(ctx *TxContext, val *Hero)

func (*Player) TruncateBags

func (p *Player) TruncateBags(ctx *TxContext, k1 int32, n int)

func (*Player) TruncateCooldowns

func (p *Player) TruncateCooldowns(ctx *TxContext, k1 int32, n int)

func (*Player) TruncateItems

func (p *Player) TruncateItems(ctx *TxContext, n int)

type Quest

type Quest struct {
	Id         int32           `json:"id,omitempty" bson:"id"`
	State      int32           `json:"state,omitempty" bson:"state"`
	Objectives map[int32]int32 `json:"objectives,omitempty" bson:"objectives"`
}

Quest 任务进度。

+k8s:deepcopy-gen=true

func (*Quest) CloneForWrite

func (q *Quest) CloneForWrite() *Quest

CloneForWrite 返回 Quest 的可写浅拷贝。

func (*Quest) DeepCopy

func (in *Quest) DeepCopy() *Quest

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Quest.

func (*Quest) DeepCopyInto

func (in *Quest) DeepCopyInto(out *Quest)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*Quest) PutId

func (q *Quest) PutId(ctx *TxContext, val int32)

func (*Quest) PutObjectives

func (q *Quest) PutObjectives(ctx *TxContext, k1 int32, val int32)

func (*Quest) PutState

func (q *Quest) PutState(ctx *TxContext, val int32)

func (*Quest) RemoveObjectives added in v1.0.1

func (q *Quest) RemoveObjectives(ctx *TxContext, k1 int32)

type Skill

type Skill struct {
	SkillId int32 `json:"skill_id,omitempty" bson:"skill_id"`
	Level   int32 `json:"level,omitempty" bson:"level"`
}

Skill 技能子结构。

+k8s:deepcopy-gen=true

func (*Skill) CloneForWrite

func (s *Skill) CloneForWrite() *Skill

CloneForWrite 返回 Skill 的可写浅拷贝。

func (*Skill) DeepCopy

func (in *Skill) DeepCopy() *Skill

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Skill.

func (*Skill) DeepCopyInto

func (in *Skill) DeepCopyInto(out *Skill)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*Skill) PutLevel

func (s *Skill) PutLevel(ctx *TxContext, val int32)

func (*Skill) PutSkillId

func (s *Skill) PutSkillId(ctx *TxContext, val int32)

type TxContext

type TxContext struct {
	// contains filtered or unexported fields
}

TxContext 单次请求作用域的 Undo 日志(单协程,无锁)。

+k8s:deepcopy-gen=false

Example (Rollback)

ExampleTxContext_rollback 演示失败路径下 Rollback 恢复聚合根状态。

player := newBenchPlayer()
beforeGold := player.Assets["gold"]

ctx := txPool.Get().(*TxContext)
ctx.Reset()
defer txPool.Put(ctx)

applySparseWrites(player, ctx)
ctx.Rollback()

if player.Assets["gold"] == beforeGold {
	fmt.Println("rolled back")
}
Output:
rolled back

func (*TxContext) Reset

func (ctx *TxContext) Reset()

Reset 清空日志并复用底层切片。

func (*TxContext) Rollback

func (ctx *TxContext) Rollback()

Rollback 倒序执行所有逆操作。

Directories

Path Synopsis
cmd
undocheck command
undoproxy-gen command
undoproxy-gen 为带 +cow:undoproxy-gen 标记的类型生成 Undo 写代理。
undoproxy-gen 为带 +cow:undoproxy-gen 标记的类型生成 Undo 写代理。
undorewrite command
undorewrite 将监控类型的裸写批量改为 undoproxy 代理调用。
undorewrite 将监控类型的裸写批量改为 undoproxy 代理调用。
internal
cowmon
Package cowmon 加载 +cow:undoproxy-gen 标记的类型图(undoproxy-gen / undocheck 共用)。
Package cowmon 加载 +cow:undoproxy-gen 标记的类型图(undoproxy-gen / undocheck 共用)。
scripts
check-pkgname command
check-pkgname 检查包内导出标识符(函数/类型/变量/方法)是否以包名开头。
check-pkgname 检查包内导出标识符(函数/类型/变量/方法)是否以包名开头。

Jump to

Keyboard shortcuts

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