Giter Club home page Giter Club logo

gotcc's Introduction

gotcc



gotcc: 纯 golang 实现的 tcc sdk 框架

📚 前言

正所谓“理论先行,实践紧随”. 使用此框架实战前,建议先行梳理 tcc 相关理论知识,做到知行合一、收放自如

📖 sdk 核心能力

实现了 txManager 事务协调器,完成 try-confirm/cancel 二阶段提交流程的组织串联

💡 tcc 技术原理篇与开源实战篇技术博客

理论篇

实战篇

🖥 接入 sop

  • 用户需要自行实现事务日志存储模块 TXStore interface,并将其注入到事务协调器 TXManager

// 事务日志存储模块
type TXStore interface {
	// 创建一条事务明细记录
	CreateTX(ctx context.Context, components ...component.TCCComponent) (txID string, err error)
	// 更新事务进度:实际更新的是每个组件的 try 请求响应结果
	TXUpdate(ctx context.Context, txID string, componentID string, accept bool) error
	// 提交事务的最终状态, 标识事务执行结果为成功或失败
	TXSubmit(ctx context.Context, txID string, success bool) error
	// 获取到所有未完成的事务
	GetHangingTXs(ctx context.Context) ([]*Transaction, error)
	// 获取指定的一笔事务
	GetTX(ctx context.Context, txID string) (*Transaction, error)
	// 锁住整个 TXStore 模块(要求为分布式锁)
	Lock(ctx context.Context, expireDuration time.Duration) error
	// 解锁TXStore 模块
	Unlock(ctx context.Context) error
}
  • 用户需要自行实现 TCC 组件 TCCComponent,并将其注册到事务协调器 TXManager

// tcc 组件
type TCCComponent interface {
	// 返回组件唯一 id
	ID() string
	// 执行第一阶段的 try 操作
	Try(ctx context.Context, req *TCCReq) (*TCCResp, error)
	// 执行第二阶段的 confirm 操作
	Confirm(ctx context.Context, txID string) (*TCCResp, error)
	// 执行第二阶段的 cancel 操作
	Cancel(ctx context.Context, txID string) (*TCCResp, error)
}

🐧 使用示例

使用单测示例代码如下. 其中有关于 txStore 模块的实现类示例,同样参见 package example

const (
	dsn      = "请输入你的 mysql dsn"
	network  = "tcp"
	address  = "请输入你的 redis ip"
	password = "请输入你的 redis 密码"
)

func Test_TCC(t *testing.T) {
	redisClient := pkg.NewRedisClient(network, address, password)
	mysqlDB, err := pkg.NewDB(dsn)
	if err != nil {
		t.Error(err)
		return
	}

	componentAID := "componentA"
	componentBID := "componentB"
	componentCID := "componentC"

	// 构造出对应的 tcc component
	componentA := NewMockComponent(componentAID, redisClient)
	componentB := NewMockComponent(componentBID, redisClient)
	componentC := NewMockComponent(componentCID, redisClient)

	// 构造出事务日志存储模块
	txRecordDAO := dao.NewTXRecordDAO(mysqlDB)
	txStore := NewMockTXStore(txRecordDAO, redisClient)

	txManager := gotcc.NewTXManager(txStore, gotcc.WithMonitorTick(time.Second))
	defer txManager.Stop()

	// 完成各组件的注册
	if err := txManager.Register(componentA); err != nil {
		t.Error(err)
		return
	}

	if err := txManager.Register(componentB); err != nil {
		t.Error(err)
		return
	}

	if err := txManager.Register(componentC); err != nil {
		t.Error(err)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
	defer cancel()
	success, err := txManager.Transaction(ctx, []*gotcc.RequestEntity{
		{ComponentID: componentAID,
			Request: map[string]interface{}{
				"biz_id": componentAID + "_biz",
			},
		},
		{ComponentID: componentBID,
			Request: map[string]interface{}{
				"biz_id": componentBID + "_biz",
			},
		},
		{ComponentID: componentCID,
			Request: map[string]interface{}{
				"biz_id": componentCID + "_biz",
			},
		},
	}...)
	if err != nil {
		t.Errorf("tx failed, err: %v", err)
		return
	}
	if !success {
		t.Error("tx failed")
		return
	}

	t.Log("success")
}

gotcc's People

Contributors

xiaoxuxiansheng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

gotcc's Issues

大佬,请问为什么在transaction方法中已经等待一个事务的所有组件更新try请求状态,而在advanceProgress方法中又调用getStatus遍历一个事务的所有组件try请求状态,怎么可能会出现TXHanging状态呢?因为在transaction中必然将try请求更新状态为fail或者success

大佬,请问为什么在transaction方法中已经等待一个事务的所有组件更新try请求状态,而在advanceProgress方法中又调用getStatus遍历一个事务的所有组件try请求状态,怎么可能会出现TXHanging状态呢?因为在transaction中必然将try请求更新状态为fail或者success,这点我很奇怪,是我代码理解错误了吗?

readme小建议

readme里的这个可以换成.,这样本地离线或者gitee也能看
https://github.com/xiaoxuxiansheng/gotcc/blob/main

大佬,请问为什么在transaction方法中已经等待一个事务的所有组件更新try请求状态,而在advanceProgress方法中又调用getStatus遍历一个事务的所有组件try请求状态,怎么可能会出现TXHanging状态呢?因为在transaction中必然将try请求更新状态为fail或者success

大佬,请问为什么在transaction方法中已经等待一个事务的所有组件更新try请求状态,而在advanceProgress方法中又调用getStatus遍历一个事务的所有组件try请求状态,怎么可能会出现TXHanging状态呢?因为在transaction中必然将try请求更新状态为fail或者success,这点我很奇怪,是我代码理解错误了吗?

代码逻辑疑问

resp, err := componentEntity.Component.Try(cctx, &component.TCCReq{ ComponentID: componentEntity.Component.ID(), TXID: txID, Data: componentEntity.Request, }) // 但凡有一个 component try 报错或者拒绝,都是需要进行 cancel 的,但会放在 advanceProgressByTXID 流程处理 if err != nil || !resp.ACK { log.ErrorContextf(cctx, "tx try failed, tx id: %s, comonent id: %s, err: %v", txID, componentEntity.Component.ID(), err) // 对对应的事务进行更新 if _err := t.txStore.TXUpdate(cctx, txID, componentEntity.Component.ID(), false); _err != nil { log.ErrorContextf(cctx, "tx updated failed, tx id: %s, component id: %s, err: %v", txID, componentEntity.Component.ID(), _err) } errCh <- fmt.Errorf("component: %s try failed", componentEntity.Component.ID()) return } // try 请求成功,但是请求结果更新到事务日志失败时,也需要视为处理失败 if err = t.txStore.TXUpdate(cctx, txID, componentEntity.Component.ID(), true); err != nil { log.ErrorContextf(cctx, "tx updated failed, tx id: %s, component id: %s, err: %v", txID, componentEntity.Component.ID(), err) errCh <- err }这段代码已经明确表示了try请求的状态必然被更新为fail或success,那getStatus方法怎么可能会返回TXHanging呢?

运行example_test报错了

github.com/xiaoxuxiansheng/[email protected]/example/dao/txrecord.go:87 sql: Scan error on column index 4, name "created_at": unsupported Scan, storing driver.Value type []uint8 into type *time.Time

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.