|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- package tm
-
- import (
- "context"
- "reflect"
- )
-
- import (
- "github.com/pkg/errors"
- )
-
- import (
- context2 "github.com/transaction-wg/seata-golang/pkg/client/context"
- "github.com/transaction-wg/seata-golang/pkg/client/proxy"
- "github.com/transaction-wg/seata-golang/pkg/util/log"
- )
-
- type GlobalTransactionServiceProxy interface {
- GetServiceProxy() interface{}
- GetMethodTransactionInfo(methodName string) *TransactionInfo
- }
-
- var (
- typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
- )
-
- func makeCallProxy(methodDesc *proxy.MethodDescriptor, txInfo *TransactionInfo) func(in []reflect.Value) []reflect.Value {
- return func(in []reflect.Value) []reflect.Value {
- var (
- args = make([]interface{}, 0)
- returnValues = make([]reflect.Value, 0)
- suspendedResourcesHolder *SuspendedResourcesHolder
- )
-
- if txInfo == nil {
- // testing phase, this problem should be resolved
- panic(errors.New("transactionInfo does not exist"))
- }
-
- inNum := len(in)
- if inNum+1 != methodDesc.ArgsNum {
- // testing phase, this problem should be resolved
- panic(errors.New("args does not match"))
- }
-
- invCtx := context2.NewRootContext(context.Background())
- for i := 0; i < inNum; i++ {
- if in[i].Type().String() == "context.Context" {
- if !in[i].IsNil() {
- // the user declared context as method's parameter
- invCtx = context2.NewRootContext(in[i].Interface().(context.Context))
- }
- }
- args = append(args, in[i].Interface())
- }
-
- tx := GetCurrentOrCreate(invCtx)
-
- defer func() {
- err := tx.Resume(suspendedResourcesHolder, invCtx)
- if err != nil {
- log.Errorf("error tx. Resume ret: %v", err)
- }
- }()
-
- switch txInfo.Propagation {
- case REQUIRED:
- case REQUIRES_NEW:
- suspendedResourcesHolder, _ = tx.Suspend(true, invCtx)
- case NOT_SUPPORTED:
- suspendedResourcesHolder, _ = tx.Suspend(true, invCtx)
- returnValues = proxy.Invoke(methodDesc, invCtx, args)
- return returnValues
- case SUPPORTS:
- if !invCtx.InGlobalTransaction() {
- returnValues = proxy.Invoke(methodDesc, invCtx, args)
- return returnValues
- }
- case NEVER:
- if invCtx.InGlobalTransaction() {
- return proxy.ReturnWithError(methodDesc, errors.Errorf("Existing transaction found for transaction marked with propagation 'never',xid = %s", invCtx.GetXID()))
- } else {
- returnValues = proxy.Invoke(methodDesc, invCtx, args)
- return returnValues
- }
- case MANDATORY:
- if !invCtx.InGlobalTransaction() {
- return proxy.ReturnWithError(methodDesc, errors.New("No existing transaction found for transaction marked with propagation 'mandatory'"))
- }
- default:
- return proxy.ReturnWithError(methodDesc, errors.Errorf("Not Supported Propagation: %s", txInfo.Propagation.String()))
- }
-
- beginErr := tx.BeginWithTimeoutAndName(txInfo.TimeOut, txInfo.Name, invCtx)
- if beginErr != nil {
- return proxy.ReturnWithError(methodDesc, errors.WithStack(beginErr))
- }
-
- returnValues = proxy.Invoke(methodDesc, invCtx, args)
-
- errValue := returnValues[len(returnValues)-1]
-
- //todo 只要出错就回滚,未来可以优化一下,某些错误才回滚,某些错误的情况下,可以提交
- if errValue.IsValid() && !errValue.IsNil() {
- rollbackErr := tx.Rollback(invCtx)
- if rollbackErr != nil {
- return proxy.ReturnWithError(methodDesc, errors.WithStack(rollbackErr))
- }
- // return returnValues with root cause error instead of fixed string
- return returnValues
- }
-
- commitErr := tx.Commit(invCtx)
- if commitErr != nil {
- return proxy.ReturnWithError(methodDesc, errors.WithStack(commitErr))
- }
-
- return returnValues
- }
- }
-
- func Implement(v GlobalTransactionServiceProxy) {
- valueOf := reflect.ValueOf(v)
- log.Debugf("[Implement] reflect.TypeOf: %s", valueOf.String())
-
- valueOfElem := valueOf.Elem()
- typeOf := valueOfElem.Type()
-
- // check incoming interface, incoming interface's elem must be a struct.
- if typeOf.Kind() != reflect.Struct {
- log.Errorf("%s must be a struct ptr", valueOf.String())
- return
- }
- serviceProxy := v.GetServiceProxy()
-
- numField := valueOfElem.NumField()
- for i := 0; i < numField; i++ {
- t := typeOf.Field(i)
- methodName := t.Name
- f := valueOfElem.Field(i)
- if f.Kind() == reflect.Func && f.IsValid() && f.CanSet() {
- outNum := t.Type.NumOut()
-
- // The latest return type of the method must be error.
- if returnType := t.Type.Out(outNum - 1); returnType != typError {
- log.Warnf("the latest return type %s of method %q is not error", returnType, t.Name)
- continue
- }
-
- methodDescriptor := proxy.Register(serviceProxy, methodName)
-
- // do method proxy here:
- f.Set(reflect.MakeFunc(f.Type(), makeCallProxy(methodDescriptor, v.GetMethodTransactionInfo(methodName))))
- log.Debugf("set method [%s]", methodName)
- }
- }
- }
|