You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

proxy.go 4.8 kB

4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package tm
  2. import (
  3. "context"
  4. "reflect"
  5. )
  6. import (
  7. "github.com/pkg/errors"
  8. )
  9. import (
  10. context2 "github.com/transaction-wg/seata-golang/pkg/client/context"
  11. "github.com/transaction-wg/seata-golang/pkg/client/proxy"
  12. "github.com/transaction-wg/seata-golang/pkg/util/log"
  13. )
  14. type GlobalTransactionServiceProxy interface {
  15. GetServiceProxy() interface{}
  16. GetMethodTransactionInfo(methodName string) *TransactionInfo
  17. }
  18. var (
  19. typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
  20. )
  21. func makeCallProxy(methodDesc *proxy.MethodDescriptor, txInfo *TransactionInfo) func(in []reflect.Value) []reflect.Value {
  22. return func(in []reflect.Value) []reflect.Value {
  23. var (
  24. args = make([]interface{}, 0)
  25. returnValues = make([]reflect.Value, 0)
  26. suspendedResourcesHolder *SuspendedResourcesHolder
  27. )
  28. if txInfo == nil {
  29. // testing phase, this problem should be resolved
  30. panic(errors.New("transactionInfo does not exist"))
  31. }
  32. inNum := len(in)
  33. if inNum+1 != methodDesc.ArgsNum {
  34. // testing phase, this problem should be resolved
  35. panic(errors.New("args does not match"))
  36. }
  37. invCtx := context2.NewRootContext(context.Background())
  38. for i := 0; i < inNum; i++ {
  39. if in[i].Type().String() == "context.Context" {
  40. if !in[i].IsNil() {
  41. // the user declared context as method's parameter
  42. invCtx = context2.NewRootContext(in[i].Interface().(context.Context))
  43. }
  44. }
  45. args = append(args, in[i].Interface())
  46. }
  47. tx := GetCurrentOrCreate(invCtx)
  48. defer func() {
  49. err := tx.Resume(suspendedResourcesHolder, invCtx)
  50. if err != nil {
  51. log.Errorf("error tx. Resume ret: %v", err)
  52. }
  53. }()
  54. switch txInfo.Propagation {
  55. case REQUIRED:
  56. case REQUIRES_NEW:
  57. suspendedResourcesHolder, _ = tx.Suspend(true, invCtx)
  58. case NOT_SUPPORTED:
  59. suspendedResourcesHolder, _ = tx.Suspend(true, invCtx)
  60. returnValues = proxy.Invoke(methodDesc, invCtx, args)
  61. return returnValues
  62. case SUPPORTS:
  63. if !invCtx.InGlobalTransaction() {
  64. returnValues = proxy.Invoke(methodDesc, invCtx, args)
  65. return returnValues
  66. }
  67. case NEVER:
  68. if invCtx.InGlobalTransaction() {
  69. return proxy.ReturnWithError(methodDesc, errors.Errorf("Existing transaction found for transaction marked with propagation 'never',xid = %s", invCtx.GetXID()))
  70. } else {
  71. returnValues = proxy.Invoke(methodDesc, invCtx, args)
  72. return returnValues
  73. }
  74. case MANDATORY:
  75. if !invCtx.InGlobalTransaction() {
  76. return proxy.ReturnWithError(methodDesc, errors.New("No existing transaction found for transaction marked with propagation 'mandatory'"))
  77. }
  78. default:
  79. return proxy.ReturnWithError(methodDesc, errors.Errorf("Not Supported Propagation: %s", txInfo.Propagation.String()))
  80. }
  81. beginErr := tx.BeginWithTimeoutAndName(txInfo.TimeOut, txInfo.Name, invCtx)
  82. if beginErr != nil {
  83. return proxy.ReturnWithError(methodDesc, errors.WithStack(beginErr))
  84. }
  85. returnValues = proxy.Invoke(methodDesc, invCtx, args)
  86. errValue := returnValues[len(returnValues)-1]
  87. //todo 只要出错就回滚,未来可以优化一下,某些错误才回滚,某些错误的情况下,可以提交
  88. if errValue.IsValid() && !errValue.IsNil() {
  89. rollbackErr := tx.Rollback(invCtx)
  90. if rollbackErr != nil {
  91. return proxy.ReturnWithError(methodDesc, errors.WithStack(rollbackErr))
  92. }
  93. // return returnValues with root cause error instead of fixed string
  94. return returnValues
  95. }
  96. commitErr := tx.Commit(invCtx)
  97. if commitErr != nil {
  98. return proxy.ReturnWithError(methodDesc, errors.WithStack(commitErr))
  99. }
  100. return returnValues
  101. }
  102. }
  103. func Implement(v GlobalTransactionServiceProxy) {
  104. valueOf := reflect.ValueOf(v)
  105. log.Debugf("[Implement] reflect.TypeOf: %s", valueOf.String())
  106. valueOfElem := valueOf.Elem()
  107. typeOf := valueOfElem.Type()
  108. // check incoming interface, incoming interface's elem must be a struct.
  109. if typeOf.Kind() != reflect.Struct {
  110. log.Errorf("%s must be a struct ptr", valueOf.String())
  111. return
  112. }
  113. serviceProxy := v.GetServiceProxy()
  114. numField := valueOfElem.NumField()
  115. for i := 0; i < numField; i++ {
  116. t := typeOf.Field(i)
  117. methodName := t.Name
  118. f := valueOfElem.Field(i)
  119. if f.Kind() == reflect.Func && f.IsValid() && f.CanSet() {
  120. outNum := t.Type.NumOut()
  121. // The latest return type of the method must be error.
  122. if returnType := t.Type.Out(outNum - 1); returnType != typError {
  123. log.Warnf("the latest return type %s of method %q is not error", returnType, t.Name)
  124. continue
  125. }
  126. methodDescriptor := proxy.Register(serviceProxy, methodName)
  127. // do method proxy here:
  128. f.Set(reflect.MakeFunc(f.Type(), makeCallProxy(methodDescriptor, v.GetMethodTransactionInfo(methodName))))
  129. log.Debugf("set method [%s]", methodName)
  130. }
  131. }
  132. }