Compare commits

...

No commits in common. 'master' and 'master_old' have entirely different histories.

100 changed files with 798 additions and 9950 deletions
Split View
  1. +0
    -42
      .asf.yaml
  2. +0
    -122
      .github/ISSUE_TEMPLATE/#0_bug_report_zh.yml
  3. +0
    -94
      .github/ISSUE_TEMPLATE/#1_feature_request_zh.yml
  4. +0
    -80
      .github/ISSUE_TEMPLATE/#2_question_zh.yml
  5. +0
    -47
      .github/ISSUE_TEMPLATE/#3_proposal_zh.yml
  6. +0
    -26
      .github/ISSUE_TEMPLATE/#4_discussion_zh.yml
  7. +0
    -122
      .github/ISSUE_TEMPLATE/0_bug_report.yml
  8. +0
    -60
      .github/ISSUE_TEMPLATE/1_feature_request.yml
  9. +0
    -80
      .github/ISSUE_TEMPLATE/2_question.yml
  10. +0
    -47
      .github/ISSUE_TEMPLATE/3_proposal.yml
  11. +0
    -26
      .github/ISSUE_TEMPLATE/4_discussion.yml
  12. +0
    -24
      .github/PULL_REQUEST_TEMPLATE.md
  13. +0
    -71
      .github/actions/labeler/labeler.yml
  14. +0
    -155
      .github/release-drafter.yml
  15. +0
    -75
      .github/workflows/build.yml
  16. +0
    -69
      .github/workflows/codeql.yml
  17. +0
    -62
      .github/workflows/golangci-lint.yml
  18. +0
    -83
      .github/workflows/integrate-test.yml
  19. +0
    -42
      .github/workflows/labeler.yml
  20. +0
    -71
      .github/workflows/license.yml
  21. +0
    -23
      .github/workflows/release-drafter.yml
  22. +0
    -50
      .github/workflows/stale.yml
  23. +4
    -26
      .gitignore
  24. +0
    -92
      .golangci.yml
  25. +0
    -78
      .licenserc.yaml
  26. +0
    -24
      .pre-commit-config.yaml
  27. +0
    -212
      CONTRIBUTING.md
  28. +0
    -210
      CONTRIBUTING_CN.md
  29. +0
    -10
      DISCLAIMER
  30. +0
    -5
      NOTICE
  31. +13
    -160
      README.md
  32. +0
    -164
      README_ZH.md
  33. +0
    -78
      changes/0.1.0.md
  34. +0
    -78
      changes/0.1.0_zh.md
  35. +0
    -156
      changes/1.0.2-RC1.md
  36. +0
    -157
      changes/1.0.2-RC1_zh.md
  37. +0
    -95
      changes/1.0.3.md
  38. +0
    -98
      changes/1.0.3_zh.md
  39. +0
    -91
      changes/1.1.0.md
  40. +0
    -92
      changes/1.1.0_zh.md
  41. +0
    -80
      changes/1.2.0.md
  42. +0
    -83
      changes/1.2.0_zh.md
  43. +0
    -173
      changes/2.0.0.md
  44. +0
    -169
      changes/2.0.0_zh.md
  45. +0
    -48
      changes/dev.md
  46. +0
    -50
      changes/dev_zh.md
  47. +0
    -22
      cmd/start.go
  48. +26
    -0
      common/xid.go
  49. +1
    -0
      docs/seata-golang.svg
  50. +10
    -108
      go.mod
  51. +31
    -1242
      go.sum
  52. +0
    -29
      goimports.sh
  53. +0
    -48
      integrate_test.sh
  54. +0
    -9
      licenses/LICENSE.tpl
  55. +186
    -0
      logging/logging.go
  56. +7
    -0
      logging/logging_test.go
  57. +0
    -80
      makefile
  58. +102
    -0
      meta/branch_status.go
  59. +36
    -0
      meta/branch_type.go
  60. +142
    -0
      meta/global_status.go
  61. +110
    -0
      meta/transaction_exception_code.go
  62. +33
    -0
      meta/transaction_role.go
  63. +28
    -0
      model/resource.go
  64. +69
    -0
      model/set.go
  65. +0
    -107
      pkg/client/client.go
  66. +0
    -254
      pkg/client/config.go
  67. +0
    -226
      pkg/client/config_test.go
  68. +0
    -18
      pkg/compressor/7z_compress.go
  69. +0
    -67
      pkg/compressor/bzip2_compress.go
  70. +0
    -40
      pkg/compressor/bzip2_compress_test.go
  71. +0
    -24
      pkg/compressor/compressor.go
  72. +0
    -53
      pkg/compressor/compressor_type.go
  73. +0
    -51
      pkg/compressor/deflate_compress.go
  74. +0
    -53
      pkg/compressor/deflate_compress_test.go
  75. +0
    -66
      pkg/compressor/gzip_compress.go
  76. +0
    -40
      pkg/compressor/gzip_compress_test.go
  77. +0
    -57
      pkg/compressor/lz4_compress.go
  78. +0
    -40
      pkg/compressor/lz4_compress_test.go
  79. +0
    -33
      pkg/compressor/none_compressor.go
  80. +0
    -59
      pkg/compressor/zip_compress.go
  81. +0
    -40
      pkg/compressor/zip_compress_test.go
  82. +0
    -44
      pkg/compressor/zstd_compress.go
  83. +0
    -48
      pkg/compressor/zstd_compress_test.go
  84. +0
    -42
      pkg/constant/context.go
  85. +0
    -26
      pkg/datasource/init.go
  86. +0
    -213
      pkg/datasource/sql/async_worker.go
  87. +0
    -129
      pkg/datasource/sql/at_resource_manager.go
  88. +0
    -272
      pkg/datasource/sql/conn.go
  89. +0
    -162
      pkg/datasource/sql/conn_at.go
  90. +0
    -240
      pkg/datasource/sql/conn_at_test.go
  91. +0
    -406
      pkg/datasource/sql/conn_xa.go
  92. +0
    -331
      pkg/datasource/sql/conn_xa_test.go
  93. +0
    -139
      pkg/datasource/sql/connector.go
  94. +0
    -140
      pkg/datasource/sql/connector_test.go
  95. +0
    -188
      pkg/datasource/sql/datasource/base/meta_cache.go
  96. +0
    -288
      pkg/datasource/sql/datasource/base/meta_cache_test.go
  97. +0
    -125
      pkg/datasource/sql/datasource/datasource_manager.go
  98. +0
    -79
      pkg/datasource/sql/datasource/mysql/meta_cache.go
  99. +0
    -222
      pkg/datasource/sql/datasource/mysql/trigger.go
  100. +0
    -190
      pkg/datasource/sql/datasource/mysql/trigger_test.go

+ 0
- 42
.asf.yaml View File

@@ -1,42 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
github:
description: "Go Implementation For Seata"
homepage: https://seata.apache.org/
labels:
- at
- tcc
- saga
- xa
enabled_merge_buttons:
squash: true
merge: false
rebase: false
dependabot_alerts: true
dependabot_updates: false
protected_branches:
master:
required_status_checks:
strict: true
required_pull_request_reviews:
dismiss_stale_reviews: true
required_approving_review_count: 1
notifications:
commits: notifications@seata.apache.org
issues: notifications@seata.apache.org
pullrequests: notifications@seata.apache.org
discussions: dev@seata.apache.org

+ 0
- 122
.github/ISSUE_TEMPLATE/#0_bug_report_zh.yml View File

@@ -1,122 +0,0 @@
name: "🐞 Bug 报告"
description: "提交 Bug 帮助我们改进"
title: "[BUG] "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
## 👋 感谢您的反馈!
请填写以下信息帮助我们更好地理解和解决问题。

- type: checkboxes
id: duplicate-check
attributes:
label: "✅ 验证清单"
description: "请确认您已经完成以下操作:"
options:
- label: "🔍 我已经搜索过 [现有 Issues](https://github.com/apache/incubator-seata-go/issues),确信这不是重复问题"
required: true

- type: markdown
attributes:
value: |
---
## 🔧 环境信息

- type: input
id: go-version
attributes:
label: "🚀 Go 版本"
description: "请提供您使用的 Go 版本"
placeholder: "例如:1.23.0"
validations:
required: true

- type: input
id: seata-go-version
attributes:
label: "📦 Seata-go 版本"
description: "请提供您使用的 seata-go 版本"
placeholder: "例如:v1.2.0"
validations:
required: true

- type: dropdown
id: platform
attributes:
label: "💾 操作系统"
description: "您使用的操作系统平台"
options:
- "🪟 Windows"
- "🍎 macOS"
- "🐧 Linux"
validations:
required: true

- type: markdown
attributes:
value: |
---
## 🐛 问题详情

- type: textarea
id: bug-description
attributes:
label: "📝 Bug 描述"
description: "请清晰简洁地描述您遇到的问题"
placeholder: |
请详细描述您遇到的 bug,包括:
• 具体的问题现象
• 错误信息(如有)
• 影响范围
validations:
required: true

- type: textarea
id: reproduction-steps
attributes:
label: "🔄 重现步骤"
description: "请提供详细的步骤来重现这个问题"
placeholder: |
请按顺序列出重现步骤:
1. 第一步...
2. 第二步...
3. 第三步...
4. 看到错误

💡 如果可能,请提供 GitHub 仓库链接或最小重现代码
validations:
required: true

- type: textarea
id: expected-behavior
attributes:
label: "✅ 预期行为"
description: "请描述您期望应该发生什么"
placeholder: "详细描述正确的行为应该是什么样的..."
validations:
required: true

- type: textarea
id: actual-behavior
attributes:
label: "❌ 实际行为"
description: "请描述实际发生了什么"
placeholder: |
详细描述实际发生的情况,包括:
• 错误消息
• 异常堆栈
• 日志输出
validations:
required: true

- type: textarea
id: possible-solution
attributes:
label: "💡 可能的解决方案"
description: "如果您对解决这个问题有想法,请在此分享"
placeholder: "分享您的想法、建议或已尝试的解决方案..."
validations:
required: false

+ 0
- 94
.github/ISSUE_TEMPLATE/#1_feature_request_zh.yml View File

@@ -1,94 +0,0 @@
name: "✨ 功能请求"
description: "提出新想法或功能建议"
title: "[FEATURE] "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
## 🚀 功能请求
感谢您为项目提供新的想法和建议!

- type: checkboxes
id: verification
attributes:
label: "✅ 验证清单"
description: "请确认您已经完成以下操作:"
options:
- label: "🔍 我已经搜索过 [现有 Issues](https://github.com/apache/incubator-seata-go/issues),确信这不是重复请求"
required: true
- label: "📋 我已经查看了 [发布说明](https://github.com/apache/incubator-seata-go/releases),确信此功能尚未实现"
required: true

- type: markdown
attributes:
value: |
---
## 💡 功能详情

- type: textarea
id: solution-description
attributes:
label: "🎯 功能描述"
description: "清晰概述您建议的功能或方法"
placeholder: |
请详细描述您希望看到的功能,包括:
• 功能的核心作用
• 预期的使用方式
• 与现有功能的关系
validations:
required: true

- type: textarea
id: use-cases
attributes:
label: "📋 使用场景"
description: "这个功能适用的典型场景和业务价值"
placeholder: |
请描述具体的使用场景:
• 在什么情况下会使用这个功能?
• 解决了什么具体问题?
• 带来什么价值?
validations:
required: true

- type: textarea
id: complexity-risks
attributes:
label: "⚖️ 复杂性与风险评估"
description: "潜在的技术挑战、实现难度或可能的风险"
placeholder: |
请考虑并描述:
• 实现难度评估
• 可能的技术挑战
• 对现有功能的影响
• 性能考虑
validations:
required: false

- type: textarea
id: external-dependencies
attributes:
label: "🔗 外部依赖"
description: "实现此功能需要的第三方工具、服务或集成"
placeholder: |
列出所需的外部依赖:
• 第三方库或框架
• 外部服务
• 特定的环境要求
validations:
required: false

- type: textarea
id: additional-context
attributes:
label: "📚 附加信息"
description: "任何其他相关的上下文、截图或参考资料"
placeholder: |
提供任何有助于理解需求的信息:
• 相关文档或标准
• 参考实现
• 设计草图或截图
validations:
required: false

+ 0
- 80
.github/ISSUE_TEMPLATE/#2_question_zh.yml View File

@@ -1,80 +0,0 @@
name: "❓ 问题咨询"
description: "提出关于项目的疑问"
title: "[QUESTION] "
labels: ["question"]
body:
- type: markdown
attributes:
value: |
## 🤔 问题咨询
我们很乐意帮助您解答关于 Seata-go 的问题!

- type: checkboxes
id: verification
attributes:
label: "✅ 验证清单"
description: "请确认您已经完成以下操作:"
options:
- label: "🔍 我已经搜索过 [现有 Issues](https://github.com/apache/incubator-seata-go/issues),确信这不是重复问题"
required: true

- type: markdown
attributes:
value: |
---
## 🔧 环境信息(可选)

- type: input
id: seata-go-version
attributes:
label: "📦 Seata-go 版本"
description: "请提供您使用的 seata-go 版本"
placeholder: "例如:v1.2.0"
validations:
required: false

- type: markdown
attributes:
value: |
---
## ❓ 问题详情

- type: textarea
id: question
attributes:
label: "💬 您的问题"
description: "请详细描述您想了解的问题"
placeholder: |
请详细描述您的问题,包括:
• 具体想了解什么?
• 遇到了什么困惑?
• 期望得到什么样的帮助?
validations:
required: true

- type: textarea
id: context
attributes:
label: "📚 背景信息"
description: "添加任何可能帮助我们回答您问题的上下文"
placeholder: |
提供相关背景信息:
• 您在做什么项目?
• 为什么需要了解这个问题?
• 您已经尝试过什么?
validations:
required: false

- type: textarea
id: related-resources
attributes:
label: "🔗 相关资源"
description: "链接到任何相关文档、代码或资源"
placeholder: |
分享相关链接或资源:
• 相关文档链接
• 代码仓库或片段
• 参考资料
validations:
required: false

+ 0
- 47
.github/ISSUE_TEMPLATE/#3_proposal_zh.yml View File

@@ -1,47 +0,0 @@
name: "📝 提案"
description: "创建一个技术提案"
title: "[提案] "
labels: ["proposal"]
body:
- type: checkboxes
id: verification
attributes:
label: "⚠️ 验证"
description: "请确认您已经完成以下操作:"
options:
- label: 我已经搜索过 [issues](https://github.com/apache/incubator-seata-go/issues),确信这不是一个重复的提案。
required: true

- type: markdown
attributes:
value: |
## 📋 提案详情
请使用此模板提交具体的功能设计提案。
如果您只想请求新功能并讨论可能的业务价值,请创建功能请求。

- type: textarea
id: proposal-summary
attributes:
label: "✨ 提案摘要"
description: "您提案的简要概述"
placeholder: "提供您的技术提案的简明摘要"
validations:
required: true

- type: textarea
id: implementation-approach
attributes:
label: "🛠️ 实现方法"
description: "应该如何实现这个提案?"
placeholder: "描述实现此提案的方法"
validations:
required: true

- type: textarea
id: additional-context
attributes:
label: "📚 附加上下文"
description: "任何其他相关信息"
placeholder: "提供可能有助于理解您提案的任何其他上下文"
validations:
required: false

+ 0
- 26
.github/ISSUE_TEMPLATE/#4_discussion_zh.yml View File

@@ -1,26 +0,0 @@
name: "💬 讨论"
description: "开始一个关于项目的讨论"
title: "[讨论] "
labels: ["discussion"]
body:
- type: markdown
attributes:
value: "## 🔄 讨论主题"
- type: textarea
id: discussion-content
attributes:
label: "讨论详情"
description: "请描述您想要讨论的内容"
placeholder: "提供关于您想讨论的项目相关事项的详细信息"
validations:
required: true

- type: textarea
id: related-context
attributes:
label: "📚 相关背景"
description: "添加任何相关的上下文或背景信息"
placeholder: "分享有助于理解此讨论的背景信息"
validations:
required: false

+ 0
- 122
.github/ISSUE_TEMPLATE/0_bug_report.yml View File

@@ -1,122 +0,0 @@
name: "🐞 Bug Report"
description: "Report a bug to help us improve"
title: "[BUG] "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
## 👋 Thank you for your feedback!
Please fill out the following information to help us understand and resolve the issue.

- type: checkboxes
id: duplicate-check
attributes:
label: "✅ Verification Checklist"
description: "Please verify that you've completed the following:"
options:
- label: "🔍 I have searched the [existing issues](https://github.com/apache/incubator-seata-go/issues) and confirmed this is not a duplicate"
required: true

- type: markdown
attributes:
value: |
---
## 🔧 Environment Information

- type: input
id: go-version
attributes:
label: "🚀 Go Version"
description: "The version of Go you're using"
placeholder: "e.g., 1.23.0"
validations:
required: true

- type: input
id: seata-go-version
attributes:
label: "📦 Seata-go Version"
description: "The version of seata-go you're using"
placeholder: "e.g: v1.2.0"
validations:
required: true

- type: dropdown
id: platform
attributes:
label: "💾 Operating System"
description: "What platform are you using?"
options:
- "🪟 Windows"
- "🍎 macOS"
- "🐧 Linux"
validations:
required: true

- type: markdown
attributes:
value: |
---
## 🐛 Issue Details

- type: textarea
id: bug-description
attributes:
label: "📝 Bug Description"
description: "A clear and concise description of what the bug is"
placeholder: |
Please describe the bug in detail, including:
• Specific problem symptoms
• Error messages (if any)
• Impact scope
validations:
required: true

- type: textarea
id: reproduction-steps
attributes:
label: "🔄 Steps to Reproduce"
description: "Please provide detailed steps to reproduce this issue"
placeholder: |
Please list the steps to reproduce:
1. First step...
2. Second step...
3. Third step...
4. See error

💡 If possible, please provide a GitHub repository link or minimal reproduction code
validations:
required: true

- type: textarea
id: expected-behavior
attributes:
label: "✅ Expected Behavior"
description: "What did you expect to happen?"
placeholder: "Describe in detail what the correct behavior should be..."
validations:
required: true

- type: textarea
id: actual-behavior
attributes:
label: "❌ Actual Behavior"
description: "What actually happened?"
placeholder: |
Describe what actually happened, including:
• Error messages
• Exception stack traces
• Log output
validations:
required: true

- type: textarea
id: possible-solution
attributes:
label: "💡 Possible Solution"
description: "If you have ideas on how to fix this issue, please share them here"
placeholder: "Share your thoughts, suggestions, or attempted solutions..."
validations:
required: false

+ 0
- 60
.github/ISSUE_TEMPLATE/1_feature_request.yml View File

@@ -1,60 +0,0 @@
name: "✨ Feature Request"
description: "Suggest an idea for this project"
title: "[FEATURE] "
labels: ["enhancement"]
body:
- type: checkboxes
id: verification
attributes:
label: "⚠️ Verification"
description: "Please verify that you've done the following:"
options:
- label: I have searched the [issues](https://github.com/apache/incubator-seata-go/issues) of this repository and believe that this is not a duplicate.
required: true
- label: I have searched the [release notes](https://github.com/apache/incubator-seata-go/releases) of this repository and believe that this is not a duplicate.
required: true

- type: textarea
id: solution-description
attributes:
label: "🎯 Solution Description"
description: "A clear overview of the proposed approach or feature."
placeholder: "Describe the solution you'd like to see implemented"
validations:
required: true

- type: textarea
id: use-cases
attributes:
label: "📋 Use Cases"
description: "Typical scenarios where this solution would be applied."
placeholder: "Describe situations where this feature would be useful"
validations:
required: true

- type: textarea
id: complexity-risks
attributes:
label: "⚖️ Complexity & Risks"
description: "Potential challenges, technical hurdles, or downsides."
placeholder: "Describe any potential challenges or concerns"
validations:
required: false

- type: textarea
id: external-dependencies
attributes:
label: "🔗 External Dependencies"
description: "Required third-party tools, services, or integrations."
placeholder: "List any external tools or services needed"
validations:
required: false

- type: textarea
id: additional-context
attributes:
label: "📘 Additional Context"
description: "Add any other context or screenshots about the feature request here."
placeholder: "Add any other relevant information here"
validations:
required: false

+ 0
- 80
.github/ISSUE_TEMPLATE/2_question.yml View File

@@ -1,80 +0,0 @@
name: "❓ Question"
description: "Ask a question about the project"
title: "[QUESTION] "
labels: ["question"]
body:
- type: markdown
attributes:
value: |
## 🤔 Question
We're happy to help answer your questions about Seata-go!

- type: checkboxes
id: verification
attributes:
label: "✅ Verification Checklist"
description: "Please verify that you've completed the following:"
options:
- label: "🔍 I have searched the [existing issues](https://github.com/apache/incubator-seata-go/issues) and confirmed this is not a duplicate"
required: true

- type: markdown
attributes:
value: |
---
## 🔧 Configuration (Optional)"

- type: input
id: seata-go-version
attributes:
label: "📦 Seata-go Version"
description: "The version of seata-go you're using"
placeholder: "e.g: v1.2.0"
validations:
required: false

- type: markdown
attributes:
value: |
---
## ❓ Question Details

- type: textarea
id: question
attributes:
label: "💬 Your Question"
description: "Please describe your question in detail"
placeholder: |
Please describe your question in detail, including:
• What specifically would you like to know?
• What confusion have you encountered?
• What kind of help are you expecting?
validations:
required: true

- type: textarea
id: context
attributes:
label: "📚 Background Information"
description: "Add any context that might help us answer your question"
placeholder: |
Provide relevant background information:
• What project are you working on?
• Why do you need to understand this?
• What have you already tried?
validations:
required: false

- type: textarea
id: related-resources
attributes:
label: "🔗 Related Resources"
description: "Link to any related documents, code, or resources"
placeholder: |
Share related links or resources:
• Relevant documentation links
• Code repositories or snippets
• Reference materials
validations:
required: false

+ 0
- 47
.github/ISSUE_TEMPLATE/3_proposal.yml View File

@@ -1,47 +0,0 @@
name: "📝 Proposal"
description: "Create a technical proposal"
title: "[PROPOSAL] "
labels: ["proposal"]
body:
- type: checkboxes
id: verification
attributes:
label: "⚠️ Verification"
description: "Please verify that you've done the following:"
options:
- label: I have searched the [issues](https://github.com/apache/incubator-seata-go/issues) of this repository and believe that this is not a duplicate.
required: true

- type: markdown
attributes:
value: |
## 📋 Proposal Details
Please use this for a concrete design proposal for functionality.
If you just want to request a new feature and discuss the possible business value, create a Feature Request instead.

- type: textarea
id: proposal-summary
attributes:
label: "✨ Proposal Summary"
description: "A brief overview of your proposal"
placeholder: "Provide a concise summary of your technical proposal"
validations:
required: true

- type: textarea
id: implementation-approach
attributes:
label: "🛠️ Implementation Approach"
description: "How should this be implemented?"
placeholder: "Describe the approach to implementing this proposal"
validations:
required: true

- type: textarea
id: additional-context
attributes:
label: "📚 Additional Context"
description: "Any other relevant information"
placeholder: "Provide any other context that might help understand your proposal"
validations:
required: false

+ 0
- 26
.github/ISSUE_TEMPLATE/4_discussion.yml View File

@@ -1,26 +0,0 @@
name: "💬 Discussion"
description: "Start a discussion about the project"
title: "[DISCUSSION] "
labels: ["discussion"]
body:
- type: markdown
attributes:
value: "## 🔄 Discussion Topic"
- type: textarea
id: discussion-content
attributes:
label: "Discussion Details"
description: "Please describe what you'd like to discuss"
placeholder: "Provide details about what you want to discuss regarding the project"
validations:
required: true

- type: textarea
id: related-context
attributes:
label: "📚 Related Context"
description: "Add any relevant context or background information"
placeholder: "Share any background information that helps frame this discussion"
validations:
required: false

+ 0
- 24
.github/PULL_REQUEST_TEMPLATE.md View File

@@ -1,24 +0,0 @@
<!-- Thanks for sending a pull request!
-->

**What this PR does**:

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note

```

+ 0
- 71
.github/actions/labeler/labeler.yml View File

@@ -1,71 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

coding:
- 'pkg/**/*'
- 'sample/**/*'
- 'test/**/*'
- 'cmd/**/*'

# directory .github
ci/cd:
- any: ['.github/**/*']

# directory sample
sample:
- any: ['sample/**/*']

test:
- any: ['test/**/*','testdata/**/*']

# directory cmd
cmd:
- any: ['cmd/**/*']

# directory changes
milestone:
- any: ['changes/**/*']


# directory pkg
common:
- any: ['pkg/common/**/*']

config:
- any: ['pkg/config/**/*']

imports:
- any: ['pkg/imports/**/*']

integration:
- any: ['pkg/integration/**/*']

protocol:
- any: ['pkg/protocol/**/*']

remoting:
- any: ['pkg/remoting/**/*']

rm:
- any: ['pkg/rm/**/*']

tc:
- any: ['pkg/tc/**/*']

tm:
- any: ['pkg/tm/**/*']


+ 0
- 155
.github/release-drafter.yml View File

@@ -1,155 +0,0 @@
name-template: '$RESOLVED_VERSION'
tag-template: '$RESOLVED_VERSION'

categories:
- title: 'alignment/seata-java'
labels:
- 'alignment/seata-java'
- title: 'bug'
labels:
- 'bug'
- title: 'ci/cd'
labels:
- 'ci/cd'
- title: 'coding'
labels:
- 'coding'
- title: 'documentation'
labels:
- 'documentation'
- title: 'enhancement'
labels:
- 'enhancement'
- title: 'good first issue'
labels:
- 'good first issue'
- title: 'integration'
labels:
- 'integration'
- title: 'milestone'
labels:
- 'milestone'
- title: 'module/at'
labels:
- 'module/at'
- title: 'module/rm'
labels:
- 'module/rm'
- title: 'module/saga'
labels:
- 'module/saga'
- title: 'module/tcc'
labels:
- 'module/tcc'
- title: 'module/tm'
labels:
- 'module/tm'
- title: 'module/xa'
labels:
- 'module/xa'
- title: 'optimise'
labels:
- 'optimise'
- title: 'protocol'
labels:
- 'protocol'
- title: 'question'
labels:
- 'question'
- title: 'refactor'
labels:
- 'refactor'
- title: 'remoting'
labels:
- 'remoting'
- title: 'rm'
labels:
- 'rm'
- title: 'task: help-wanted'
labels:
- 'task: help-wanted'
- title: 'tm'
labels:
- 'tm'

change-template: '- $TITLE (#$NUMBER)'
change-title-escapes: '\<*_&'
exclude-contributors:
- dependabot
- dependabot[bot]

version-resolver:
major:
labels:
- 'enhancement'
- 'module/at'
minor:
labels:
- 'enhancement'
- 'module/saga'
patch:
labels:
- 'bug'
- 'documentation'
- 'refactor'
default: patch

template: |
$CHANGES

**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
**Contributors**: $CONTRIBUTORS

autolabeler:
- label: 'documentation'
files:
- '*.md'
- 'docs/**'
title:
- '/(docs?|typo|doc:|\[doc\]|comment)/i'

- label: 'bug'
title:
- '/(fix|bug|error|fail|correct|missing)/i'
- '/(race|deadlock)/i'

- label: 'enhancement'
title:
- '/(feat|feature|add|implement|create)/i'

- label: 'refactor'
title:
- '/(refactor|cleanup|deprecate|remove|unused)/i'

- label: 'ci/cd'
files:
- '.github/workflows/**'
title:
- '/(ci|cd|workflow|pipeline)/i'

- label: 'coding'
title:
- '/(bump|upgrade|dependencies?)/i'
files:
- 'go.mod'
- 'go.sum'

- label: 'question'
title:
- '/(question|ask|inquiry)/i'

- label: 'good first issue'
title:
- '/(good first issue|easy|beginner|help wanted)/i'

- label: 'module/saga'
title:
- '/(saga)/i'

- label: 'module/at'
title:
- '/(at)/i'
- label: 'module/xa'
title:
- '/(xa)/i'

+ 0
- 75
.github/workflows/build.yml View File

@@ -1,75 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# This is a workflow to help you test the unit case and show codecov

name: "build and codecov"

on:
push:
branches: [ master ]
pull_request:
branches: ["*"]

jobs:
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
strategy:
matrix:
golang:
- 1.20

services:
mysql:
image: mysql:8.0
env:
# The MySQL docker container requires these environment variables to be set
# so we can create and migrate the test database.
# See: https://hub.docker.com/_/mysql
MYSQL_DATABASE: seata_go_test
MYSQL_ROOT_PASSWORD: seata_go
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

steps:
- name: "set up go"
uses: actions/setup-go@v3
with:
go-version: 1.20.14


# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: "checkout ${{ github.ref }}"
uses: actions/checkout@v3
with:
fetch-depth: 2

- name: "run go build"
run: go build -v ./...

- name: "run go test and out codecov"
run: go test -v ./... -race -coverprofile=coverage.out -covermode=atomic

- name: "upload coverage"
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
version: v0.6.0
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

+ 0
- 69
.github/workflows/codeql.yml View File

@@ -1,69 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#

name: CodeQL

on:
pull_request:
# The branches below must be a subset of the branches above
branches: "*"

permissions:
contents: read

jobs:
analyse:
permissions:
actions: read # for github/codeql-action/init to get workflow details
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/autobuild to send a status report
name: Analyse
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# Analysis
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2


+ 0
- 62
.github/workflows/golangci-lint.yml View File

@@ -1,62 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# This is a basic workflow to help you get started with Actions

name: "golang ci lint"

# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: "*"

# Allows you to run this workflow manually from the Actions tab
# workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
strategy:
matrix:
golang:
- 1.20.0

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- name: "set up go"
uses: actions/setup-go@v3
with:
go-version: 1.20.0

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: "checkout ${{ github.ref }}"
uses: actions/checkout@v3

- name: "golang ci lint"
uses: golangci/golangci-lint-action@v3.1.0
with:
version: v1.51.0
args: --timeout=10m
skip-go-installation: true
skip-cache: true
skip-pkg-cache: true

+ 0
- 83
.github/workflows/integrate-test.yml View File

@@ -1,83 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: "integrate-test"

on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: "*"

permissions:
contents: read

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
test:
# The type of runner that the job will run on
name: test
runs-on: ubuntu-latest
strategy:
matrix:
golang:
- 1.20

steps:

- name: "set up go"
uses: actions/setup-go@v3
with:
go-version: 1.20.0

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: "checkout ${{ github.ref }}"
uses: actions/checkout@v3

- name: Cache dependencies
uses: actions/cache@v3
with:
# Cache
path: ~/go/pkg/mod
# Cache key
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# An ordered list of keys to use for restoring the cache if no cache hit occurred for key
restore-keys: |
${{ runner.os }}-go-

- name: Set up Docker
uses: docker/setup-buildx-action@v2

- name: Install Docker Compose
run: |
curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version

# This step only runs when the event type is a pull_request
- name: Integrate Test
if: ${{ github.event_name == 'pull_request' }}
run: |
chmod +x integrate_test.sh && [[ -n "${{github.event.pull_request.head.repo.full_name}}" ]] && [[ -n "${{github.event.pull_request.head.sha}}" ]] && [[ -n "${{github.base_ref}}" ]] && ./integrate_test.sh ${{github.event.pull_request.head.repo.full_name}} ${{github.event.pull_request.head.sha}} ${{github.base_ref}}

# This step only runs when the event type is a push
- name: Integrate Test
if: ${{ github.event_name == 'push' }}
run: |
chmod +x integrate_test.sh && ./integrate_test.sh $GITHUB_REPOSITORY $GITHUB_SHA $GITHUB_BASE_REF

+ 0
- 42
.github/workflows/labeler.yml View File

@@ -1,42 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# This workflow will help me to auto set up label for pull request

name: "pull request labeler"
on:
- pull_request_target

jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: "checkout ${{ github.ref }}"
uses: actions/checkout@v3
with:
persist-credentials: false
submodules: true

- name: "labeler"
uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/actions/labeler/labeler.yml
sync-labels: true

+ 0
- 71
.github/workflows/license.yml View File

@@ -1,71 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: License checker

on:
pull_request:
branches: [ master ]
schedule:
- cron: "0 18 * * *" # TimeZone: UTC 0

jobs:
license-header:
name: License header
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Check license header
uses: apache/skywalking-eyes@985866ce7e324454f61e22eb2db2e998db09d6f3
with:
log: info
config: .licenserc.yaml
mode: check

dependency-license:
name: Dependency licenses
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: "1.20"
- name: Check Dependencies Licenses
run: |
mkdir -p ./dist-material/
cp ./licenses/LICENSE.tpl ./dist-material/LICENSE.tpl
go install github.com/apache/skywalking-eyes/cmd/license-eye@47febf5
license-eye dependency resolve --summary ./dist-material/LICENSE.tpl || exit 1
if [ -f "./dist-material/LICENSE)" ]; then
echo "echo LICENSE check"
cat ./dist-material/LICENSE
fi
- name: Check Dependencies Licenses Invalid
run: |
go install github.com/apache/skywalking-eyes/cmd/license-eye@47febf5
if [ ! -z "$(license-eye dependency check -v error | grep 'GPL\|LGPL\|ERROR')" ]; then
echo "GPL or LGPL dependency LICENSE exists"
license-eye dependency check -v error | grep 'GPL\|LGPL\|ERROR'
exit 1
fi

+ 0
- 23
.github/workflows/release-drafter.yml View File

@@ -1,23 +0,0 @@
name: Release Drafter

on:
push:
branches:
- unstable
pull_request_target:
types: [ opened, reopened, synchronize ]

permissions:
contents: write
pull-requests: write

jobs:
update_release_draft:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

+ 0
- 50
.github/workflows/stale.yml View File

@@ -1,50 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#


# https://github.com/actions/stale
name: 'close the stale content'
on:
schedule:
- cron: '0 0 * * *'
permissions:
pull-requests: write
issues: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
#never automatically stale issues
days-before-issue-stale: -1
#never automatically close issues
days-before-issue-close: -1
exempt-issue-labels: 'feature'
stale-issue-message: >
This issue has not been active for the past 4 weeks, so we will automatically close this issue after 7 days
close-issue-message: >
This issue has not been active for the past 5 weeks, so we are closing it now

#never automatically stale pr
days-before-pr-stale: -1
#never automatically close pr
days-before-pr-close: -1
stale-pr-message: >
This PR has not been active for the past 12 weeks, so we will automatically close this issue after seven days
close-pr-message: >
This PR has not had any activity for the past 13 weeks, so we are now closing it down

+ 4
- 26
.gitignore View File

@@ -1,23 +1,3 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Idea configuration file
.idea/

# Binaries for programs and plugins
*.exe
*.exe~
@@ -31,11 +11,9 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# build products
dist/

# Dependency directories (remove the comment below to include it)
# vendor/
.vscode
.codecc
vendor
.idea/
vendor/
root.data
root.data.1

+ 0
- 92
.golangci.yml View File

@@ -1,92 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

linters-settings:
govet:
shadow: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 10
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2
depguard:
list-type: blacklist
packages:
# logging is allowed only by logutils.Log, logrus
# is allowed to use only in logutils package
- github.com/sirupsen/logrus
misspell:
locale: US
lll:
line-length: 140
goimports:
local-prefixes: github.com/golangci/golangci-lint
gocritic:
enabled-tags:
- performance
- style
- experimental
disabled-checks:
- wrapperFunc

linters:
disable-all: true
enable:
- govet
- staticcheck
- ineffassign
- misspell
- asciicheck
- bodyclose
- rowserrcheck
- gofmt
- durationcheck
- sqlclosecheck

run:

issues:
exclude-dirs:
- test/testdata_etc
- pkg/golinters/goanalysis/(checker|passes)
exclude-rules:
- text: "weak cryptographic primitive"
linters:
- gosec
- linters:
- staticcheck
text: "SA1019:"
- path: _test\.go
linters:
- errcheck
- gosec
- rowserrcheck
- govet

# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.57.x # use the fixed version to not introduce new linters unexpectedly
prepare:
- echo "here I can run custom commands, but no preparation needed for this repo"


+ 0
- 78
.licenserc.yaml View File

@@ -1,78 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

header: # `header` section is configurations for source codes license header.
license:
spdx-id: Apache-2.0 # the spdx id of the license, it's convenient when your license is standard SPDX license.
copyright-owner: Apache Software Foundation # the copyright owner to replace the [owner] in the `spdx-id` template.
content: | # `license` will be used as the content when `fix` command needs to insert a license header.
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# `pattern` is optional regexp if all the file headers are the same as `license` or the license of `spdx-id` and `copyright-owner`.
pattern: |
Licensed to the Apache Software Foundation under one or more contributor
license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright
ownership. The Apache Software Foundation licenses this file to you under
the Apache License, Version 2.0 \(the "License"\); you may
not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
paths: # `paths` are the path list that will be checked (and fixed) by license-eye, default is ['**'].
- '**'

paths-ignore: # `paths-ignore` are the path list that will be ignored by license-eye.
- 'licenses'
- '**/go.mod'
- '**/go.sum'
- 'LICENSE'
- 'DISCLAIMER'
- 'NOTICE'
- '.github'
comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`.

language:
Go:
extensions:
- ".go"
comment_style_id: SlashAsterisk

# license-location-threshold specifies the index threshold where the license header can be located,
# after all, a "header" cannot be TOO far from the file start.
license-location-threshold: 80

dependency:
files:
- go.mod

+ 0
- 24
.pre-commit-config.yaml View File

@@ -1,24 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: http://github.com/golangci/golangci-lint
rev: v1.42.1
hooks:
- id: golangci-lint

+ 0
- 212
CONTRIBUTING.md View File

@@ -1,212 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Contributing to seata-go

It is warmly welcomed if you have interest to hack on seata-go. First, we encourage this kind of willing very much. And here is a list of contributing guide for you.

[[中文贡献文档](./CONTRIBUTING_CN.md)]

## Topics

* [Reporting general issues](#reporting-general-issues)
* [Code and doc contribution](#code-and-doc-contribution)
* [Test case contribution](#test-case-contribution)
* [Engage to help anything](#engage-to-help-anything)
* [Code Style](#code-style)

## Reporting general issues

To be honest, we regard every user of seata-go as a very kind contributor. After experiencing seata-go, you may have some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/apache/incubator-seata-go/issues/new/choose).

Since we collaborate project seata-go in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one.

To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](./.github/ISSUE_TEMPLATE) for issue reporters. Please **BE SURE** to follow the instructions to fill fields in template.

There are a lot of cases when you could open an issue:

* bug report
* feature request
* performance issues
* feature proposal
* feature design
* help wanted
* doc incomplete
* test improvement
* any questions on project
* and so on

Also we must remind that when filling a new issue, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on.

## Code and doc contribution

Every action to make project seata-go better is encouraged. On GitHub, every improvement for seata-go could be via a PR (short for pull request).

* If you find a typo, try to fix it!
* If you find a bug, try to fix it!
* If you find some redundant codes, try to remove them!
* If you find some test cases missing, try to add them!
* If you could enhance a feature, please **DO NOT** hesitate!
* If you find code implicit, try to add comments to make it clear!
* If you find code ugly, try to refactor that!
* If you can help to improve documents, it could not be better!
* If you find document incorrect, just do it and fix that!
* ...

Actually it is impossible to list them completely. Just remember one principle:

> WE ARE LOOKING FORWARD TO ANY PR FROM YOU.

Since you are ready to improve seata-go with a PR, we suggest you could take a look at the PR rules here.

* [Workspace Preparation](#workspace-preparation)
* [Branch Definition](#branch-definition)
* [Commit Rules](#commit-rules)
* [PR Description](#pr-description)

### Workspace Preparation

To put forward a PR, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps:

1. **FORK** seata-go to your repository. To make this work, you just need to click the button Fork in right-left of [seata/seata](https://github.com/apache/incubator-seata-go) main page. Then you will end up with your repository in `https://github.com/<your-username>/seata-go`, in which `your-username` is your GitHub username.

1. **CLONE** your own repository to develop locally. Use `git clone git@github.com:<your-username>/seata-go.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make.

1. **Set Remote** upstream to be `git@github.com:apache/seata-go.git` using the following two commands:

```bash
git remote add upstream git@github.com:apache/seata-go.git
git remote set-url --push upstream no-pushing
```

With this remote setting, you can check your git remote configuration like this:

```shell
$ git remote -v
origin git@github.com:<your-username>/seata-go.git (fetch)
origin git@github.com:<your-username>/seata-go.git (push)
upstream git@github.com:apache/seata-go.git (fetch)
upstream no-pushing (push)
```

Adding this, we can easily synchronize local branches with upstream branches.

### Branch Definition

Right now we assume every contribution via pull request is for [branch develop](https://github.com/apache/incubator-seata-go/tree/master) in seata-go. Before contributing, be aware of branch definition would help a lot.

As a contributor, keep in mind again that every contribution via pull request is for branch develop. While in project seata-go, there are several other branches, we generally call them release branches(such as 0.6.0,0.6.1), feature branches, hotfix branches and master branch.

When officially releasing a version, there will be a release branch and named with the version number.

After the release, we will merge the commit of the release branch into the master branch.

When we find that there is a bug in a certain version, we will decide to fix it in a later version or fix it in a specific hotfix version. When we decide to fix the hotfix version, we will checkout the hotfix branch based on the corresponding release branch, perform code repair and verification, and merge it into the develop branch and the master branch.

For larger features, we will pull out the feature branch for development and verification.


### Commit Rules

Actually in seata-go, we take two rules serious when committing:

* [Commit Message](#commit-message)
* [Commit Content](#commit-content)

#### Commit Message

Commit message could help reviewers better understand what is the purpose of submitted PR. It could help accelerate the code review procedure as well. We encourage contributors to use **EXPLICIT** commit message rather than ambiguous message. In general, we advocate the following commit message type:

* docs: xxxx. For example, "docs: add docs about seata-go cluster installation".
* feature: xxxx.For example, "feature: support oracle in AT mode".
* bugfix: xxxx. For example, "bugfix: fix panic when input nil parameter".
* optimize: xxxx. For example, "optimize: simplify to make codes more readable".
* test: xxx. For example, "test: add unit test case for func InsertIntoArray".
* other readable and explicit expression ways.

On the other side, we discourage contributors from committing message like the following ways:

* ~~fix bug~~
* ~~update~~
* ~~add doc~~

If you get lost, please see [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/) for a start.

#### Commit Content

Commit content represents all content changes included in one commit. We had better include things in one single commit which could support reviewer's complete review without any other commits' help. In another word, contents in one single commit can pass the CI to avoid code mess. In brief, there are three minor rules for us to keep in mind:

* avoid very large change in a commit;
* complete and reviewable for each commit.
* check git config(`user.name`, `user.email`) when committing to ensure that it is associated with your GitHub ID.

```bash
git config --get user.name
git config --get user.email
```

* when submitting pr, please add a brief description of the current changes to the dev.md file under the 'changes/' folder


In addition, in the code change part, we suggest that all contributors should read the [code style of seata-go](#code-style).

No matter commit message, or commit content, we do take more emphasis on code review.


#### Format your code

Before submitting the code, execute the script of formatting the code under the project root directory:

~~~shell
sh goimports.sh
~~~

### PR Description

PR is the only way to make change to seata-go project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](./.github/pull-request-template.md) to finish the pull request.

## Test case contribution

Any test case will be welcomed. Currently, seata-go functional test cases are of high priority.

- For unit tests, create a XXX_test.go file in the file directory of go file

## Engage to help anything

We choose GitHub as the primary place for seata-go to collaborate. So the latest updates of seata-go are always here. Although contributions via PR is an explicit way to help, we still call for any other ways.

* reply to other's issues if you could;
* help solve other user's problems;
* help review other's PR design;
* help review other's codes in PR;
* discuss about seata-go to make things clearer;
* advocate seata-go technology beyond GitHub;
* write blogs on seata-go and so on.


## Code Style

Seata-go code style reference [uber-go/guide](https://github.com/uber-go/guide) 。


### IDE Plugin Install(not necessary)

*It is not necessary to install, if you want to find a problem when you are coding.*

install go fmt 和 goimports plugin,detailed reference:https://github.com/golang/tools


In a word, **ANY HELP IS CONTRIBUTION.**

+ 0
- 210
CONTRIBUTING_CN.md View File

@@ -1,210 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# 为 seata-go 做贡献

如果你有兴趣为 seata-go 贡献代码,我们会热烈欢迎。首先,我们非常鼓励这种意愿。这是为您提供的贡献指南列表。

[[English Contributing Document](./CONTRIBUTING.md)]

## 话题

* [报告一般问题](#报告一般问题)
* [代码和文档贡献](#代码和文档贡献)
* [测试用例贡献](#测试用例贡献)
* [参与帮助任何事情](#参与帮助任何事情)
* [代码风格](#代码风格)

## 报告一般问题

老实说我们把每一个 seata-go 用户都视为非常善良的贡献者。在体验了 seata-go 之后,您可能会对项目有一些反馈。然后随时通过 [NEW ISSUE](https://github.com/apache/incubator-seata-go/issues/new/choose)打开一个问题。

因为我们在一个分布式的方式合作项目 seata-go,我们欣赏写得很好的,详细的,准确的问题报告。为了让沟通更高效,我们希望每个人都可以搜索您的问题是否在搜索列表中。如果您发现它存在,请在现有问题下的评论中添加您的详细信息,而不是打开一个全新的问题。

为了使问题细节尽可能标准,我们为问题报告者设置了一个[问题模板](./.github/ISSUE_TEMPLATE) 请务必按照说明填写模板中的字段。

有很多情况你可以打开一个问题:

* 错误报告
* 功能要求
* 性能问题
* 功能提案
* 功能设计
* 需要帮助
* 文档不完整
* 测试改进
* 关于项目的任何问题
* 等等

另外我们必须提醒的是,在填写新问题时,请记住从您的帖子中删除敏感数据。敏感数据可能是密码、密钥、网络位置、私人业务数据等。

## 代码和文档贡献

鼓励采取一切措施使 seata-go 项目变得更好。在 GitHub 上,seata-go 的每项改进都可以通过 PR(Pull Request 的缩写)实现。

* 如果您发现错别字,请尝试修复它!
* 如果您发现错误,请尝试修复它!
* 如果您发现一些多余的代码,请尝试删除它们!
* 如果您发现缺少一些测试用例,请尝试添加它们!
* 如果您可以增强功能,请**不要**犹豫!
* 如果您发现代码晦涩难懂,请尝试添加注释以使其更加易读!
* 如果您发现代码丑陋,请尝试重构它!
* 如果您能帮助改进文档,那就再好不过了!
* 如果您发现文档不正确,只需执行并修复它!
* ...

实际上不可能完整地列出它们。记住一个原则:

> 我们期待您的任何PR。

由于您已准备好通过 PR 改进 seata-go,我们建议您可以在此处查看 PR 规则。

* [工作区准备](#工作区准备)
* [分支定义](#分支定义)
* [提交规则](#提交规则)
* [PR说明](#PR说明)

### 工作区准备

为了提出 PR,我们假设你已经注册了一个 GitHub ID。然后您可以通过以下步骤完成准备工作:

1. **FORK** seata-go 到您的存储库。要完成这项工作,您只需单击 [apache/seata-go](https://github.com/apache/incubator-seata-go) 主页右侧的 Fork 按钮。然后你将在 中得到你的存储库`https://github.com/<your-username>/seata-go`,其中 your-username 是你的 GitHub 用户名。

2. **克隆** 您自己的存储库以在本地开发. 用于 `git clone git@github.com:<your-username>/seata-go.git` 将存储库克隆到本地计算机。 然后您可以创建新分支来完成您希望进行的更改。

3. **设置远程** 将上游设置为 `git@github.com:apache/seata-go.git` 使用以下两个命令:

```bash
git remote add upstream git@github.com:apache/seata-go.git
git remote set-url --push upstream no-pushing
```

使用此远程设置,您可以像这样检查您的 git 远程配置:

```shell
$ git remote -v
origin git@github.com:<your-username>/seata-go.git (fetch)
origin git@github.com:<your-username>/seata-go.git (push)
upstream git@github.com:apache/seata-go.git (fetch)
upstream no-pushing (push)
```

添加这个,我们可以轻松地将本地分支与上游分支同步。

### 分支定义

现在我们假设通过拉取请求的每个贡献都是针对 seata-go 中的 [开发分支](https://github.com/apache/incubator-seata-go/tree/master) 。在贡献之前,请注意分支定义会很有帮助。

作为贡献者,请再次记住,通过拉取请求的每个贡献都是针对分支开发的。而在 seata-go 项目中,还有其他几个分支,我们一般称它们为 release 分支(如0.6.0、0.6.1)、feature 分支、hotfix 分支和 master 分支。

当正式发布一个版本时,会有一个发布分支并以版本号命名。

在发布之后,我们会将发布分支的提交合并到主分支中。

当我们发现某个版本有 bug 时,我们会决定在以后的版本中修复它,或者在特定的 hotfix 版本中修复它。当我们决定修复 hotfix 版本时,我们会根据对应的 release 分支 checkout hotfix 分支,进行代码修复和验证,合并到 develop 分支和 master 分支。

对于较大的功能,我们将拉出功能分支进行开发和验证。


### 提交规则

实际上,在 seata-go 中,我们在提交时会认真对待两条规则:

* [提交消息](#提交消息)
* [提交内容](#提交内容)

#### 提交消息

提交消息可以帮助审稿人更好地理解提交 PR 的目的是什么。它还可以帮助加快代码审查过程。我们鼓励贡献者使用显式的提交信息,而不是模糊的信息。一般来说,我们提倡以下提交消息类型:

* docs: xxxx. For example, "docs: add docs about seata-go cluster installation".
* feature: xxxx.For example, "feature: support oracle in AT mode".
* bugfix: xxxx. For example, "bugfix: fix panic when input nil parameter".
* optimize: xxxx. For example, "optimize: simplify to make codes more readable".
* test: xxx. For example, "test: add unit test case for func InsertIntoArray".
* 其他可读和显式的表达方式。

另一方面,我们不鼓励贡献者通过以下方式提交消息:

* ~~修复错误~~
* ~~更新~~
* ~~添加文档~~

如果你不知道该怎么做,请参阅 [如何编写 Git 提交消息](http://chris.beams.io/posts/git-commit/) 作为开始。

#### 提交内容

提交内容表示一次提交中包含的所有内容更改。我们最好在一次提交中包含可以支持审阅者完整审查的内容,而无需任何其他提交的帮助。换句话说,一次提交中的内容可以通过 CI 以避免代码混乱。简而言之,我们需要牢记三个小规则:

* 避免在提交中进行非常大的更改;
* 每次提交都完整且可审查。
* 提交时检查 git config(`user.name`, `user.email`) 以确保它与您的 GitHub ID 相关联。

```bash
git config --get user.name
git config --get user.email
```

* 提交pr时,请在'changes/'文件夹下的dev.md文件中添加当前更改的简要说明


另外,在代码变更部分,我们建议所有贡献者阅读 seata-go 的 [代码风格](#代码风格)。

无论是提交信息,还是提交内容,我们都更加重视代码审查。

#### 格式化代码

提交代码之前,在项目根目录下执行格式化代码的脚本:

~~~shell
sh goimports.sh
~~~


### PR说明

PR 是更改 seata-go 项目文件的唯一方法。为了帮助审查人更好地理解你的目的,PR 描述不能太详细。我们鼓励贡献者遵循 [PR 模板](./.github/PULL_REQUEST_TEMPLATE.md) 来完成拉取请求。

## 测试用例贡献

任何测试用例都会受到欢迎。目前,seata-go 功能测试用例是高优先级的。

* 对于单元测试,在文件目录下创建一个 xxx_test.go 文件即可

## 参与帮助任何事情

我们选择 GitHub 作为 seata-go 协作的主要场所。所以 seata-go 的最新更新总是在这里。尽管通过 PR 贡献是一种明确的帮助方式,但我们仍然呼吁其他方式。

* 如果可以的话,回复别人的问题;
* 帮助解决其他用户的问题;
* 帮助审查他人的 PR 设计;
* 帮助审查其他人在 PR 中的代码;
* 讨论 seata-go以使事情更清楚;
* 在Github之外宣传 seata-go 技术;
* 写关于 seata-go 的博客等等。


## 代码风格

Seata-go 代码风格参考[uber-go/guide](https://github.com/uber-go/guide) 。


### IDE插件安装(非必须)

*没有必要安装,如果你想在编码的时候发现问题。*

安装 go fmt 和 goimports 插件,详情参考:https://github.com/golang/tools

+ 0
- 10
DISCLAIMER View File

@@ -1,10 +0,0 @@
Apache Seata (incubating) is an effort undergoing incubation at the Apache
Software Foundation (ASF), sponsored by the Apache Incubator PMC.

Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.

While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.

+ 0
- 5
NOTICE View File

@@ -1,5 +0,0 @@
Apache Seata (Incubating)
Copyright 2023-2025 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

+ 13
- 160
README.md View File

@@ -1,160 +1,13 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div style="align: center">
<img src="https://img.alicdn.com/imgextra/i1/O1CN011z0JfQ2723QgDiWuH_!!6000000007738-2-tps-1497-401.png" height="100" width="426"/>
</div>

# Seata-go: Simple Extensible Autonomous Transaction Architecture(Go version)

[![CI](https://github.com/apache/incubator-seata-go/actions/workflows/build.yml/badge.svg)](https://github.com/apache/incubator-seata-go/actions/workflows/build.yml) [![license](https://img.shields.io/github/license/apache/incubator-seata-go.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) [![codecov](https://codecov.io/gh/apache/incubator-seata-go/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/incubator-seata-go) [简体中文 ZH](./README_ZH.md)

## What is Seata-go?

Apache Seata(incubating) is a very mature distributed transaction framework, and is the de facto standard platform for distributed transaction technology in the Java field. Seata-go is the implementation version of go language in Seata multilingual ecosystem, which realizes the interoperability between Java and Go, so that Go developers can also use seata-go to realize distributed transactions. Please visit the [official website of Seata](https://seata.apache.org/) to view the quick start and documentation.

The principle of seata-go is consistent with that of Seata-java, which is composed of TM, RM and TC. The functions of TC are reused in Java, and the functions of TM and RM have been connected with Seata-java.
### Distributed Transaction Problem in Microservices

Let's imagine a traditional monolithic application. Its business is built up with 3 modules. They use a single local data source.

Naturally, data consistency will be guaranteed by the local transaction.

![Monolithic App](https://img.alicdn.com/imgextra/i3/O1CN01FTtjyG1H4vvVh1sNY_!!6000000000705-0-tps-1106-678.jpg)

Things have changed in a microservices architecture. The 3 modules mentioned above are designed to be 3 services on top of 3 different data sources ([Pattern: Database per service](http://microservices.io/patterns/data/database-per-service.html)). Data consistency within every single service is naturally guaranteed by the local transaction.

**But how about the whole business logic scope?**

![Microservices Problem](https://img.alicdn.com/imgextra/i1/O1CN01DXkc3o1te9mnJcHOr_!!6000000005926-0-tps-1268-804.jpg)

### How Seata-go do?

Seata-go is just a solution to the problem mentioned above.

![Seata solution](https://img.alicdn.com/imgextra/i1/O1CN01FheliH1k5VHIRob3p_!!6000000004632-0-tps-1534-908.jpg)

Firstly, how to define a **Distributed Transaction**?

We say, a **Distributed Transaction** is a **Global Transaction** which is made up with a batch of **Branch Transaction**, and normally **Branch Transaction** is just **Local Transaction**.

![Global & Branch](https://cdn.nlark.com/lark/0/2018/png/18862/1545015454979-a18e16f6-ed41-44f1-9c7a-bd82c4d5ff99.png)

There are three roles in Seata-go:

- **Transaction Coordinator(TC):** Maintain status of global and branch transactions, drive the global commit or rollback.
- **Transaction Manager(TM):** Define the scope of global transaction: begin a global transaction, commit or rollback a global transaction.
- **Resource Manager(RM):** Manage resources that branch transactions working on, talk to TC for registering branch transactions and reporting status of branch transactions, and drive the branch transaction commit or rollback.

![Model](https://cdn.nlark.com/lark/0/2018/png/18862/1545013915286-4a90f0df-5fda-41e1-91e0-2aa3d331c035.png)

A typical lifecycle of Seata-go managed distributed transaction:

1. TM asks TC to begin a new global transaction. TC generates an XID representing the global transaction.
2. XID is propagated through microservices' invoke chain.
3. RM registers local transaction as a branch of the corresponding global transaction of XID to TC.
4. TM asks TC for committing or rollbacking the corresponding global transaction of XID.
5. TC drives all branch transactions under the corresponding global transaction of XID to finish branch committing or rollbacking.

![Typical Process](https://cdn.nlark.com/lark/0/2018/png/18862/1545296917881-26fabeb9-71fa-4f3e-8a7a-fc317d3389f4.png)

For more details about principle and design, please go to [Seata wiki page](https://github.com/apache/incubator-seata/wiki).

### History

##### Alibaba

- **TXC**: Taobao Transaction Constructor. Alibaba middleware team started this project since 2014 to meet the distributed transaction problems caused by application architecture change from monolithic to microservices.
- **GTS**: Global Transaction Service. TXC as an Aliyun middleware product with new name GTS was published since 2016.
- **Fescar**: we started the open source project Fescar based on TXC/GTS since 2019 to work closely with the community in the future.


##### Ant Financial

- **XTS**: Extended Transaction Service. Ant Financial middleware team developed the distributed transaction middleware since 2007, which is widely used in Ant Financial and solves the problems of data consistency across databases and services.

- **DTX**: Distributed Transaction Extended. Since 2013, XTS has been published on the Ant Financial Cloud, with the name of DTX .


##### Seata Community

- **Seata** :Simple Extensible Autonomous Transaction Architecture. Ant Financial joins Fescar, which make it to be a more neutral and open community for distributed transaction, and Fescar be renamed to Seata.



## How to run?

```go
go get seata.apache.org/seata-go@v2.0.0

```

if you want to know how to use and integrate seata-go, please refer to [apache/seata-go-samples](https://github.com/apache/incubator-seata-go-samples)

## How to find the latest version
Visit Seata-Go's GitHub Releases page

Open:
https://github.com/seata/seata-go/releases

The latest tag / release is the latest stable version.


## Documentation


You can view the full documentation from Seata Official Website: [Seata Website page](https://seata.apache.org/zh-cn/docs/overview/what-is-seata).

## Reporting bugs

Please follow the [template](.github/ISSUE_TEMPLATE/BUG_REPORT_TEMPLATE.md) for reporting any issues.

## Security

Please do not use our public issue tracker but refer to our [security policy](https://github.com/apache/incubator-seata/blob/2.x/SECURITY.md)

## Contributing

Seata-go is currently in the construction stage. Welcome colleagues in the industry to join the group and work with us to promote the construction of seata-go! If you want to contribute code to seata-go, you can refer to the [**code contribution Specification**](./CONTRIBUTING_CN.md) document to understand the specifications of the community, or you can join our community DingTalk group: 33069364 and communicate together!


## Contact

* Mailing list:
* dev@seata.apache.org , for dev/user discussion. [subscribe](mailto:dev-subscribe@seata.apache.org), [unsubscribe](mailto:dev-unsubscribe@seata.apache.org), [archive](https://lists.apache.org/list.html?dev@seata.apache.org)
* Online chat:

| Dingtalk group | Wechat official account | QQ group | Wechat assistant |
|:---------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------:|
| <img src="https://seata.apache.org/zh-cn/assets/images/dingtalk-group-67f42c9466fb2268b6927bb16b549d6c.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/wechat-official-467d10305f5449e6b2096e65d23a9d02.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/qq-group-8d8a89699cdb9ba8818364069475ba96.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/wechat-f8a87a96973942b826e32d1aed9bc8d9.jpg" width="150" /> |

## Seata ecosystem

* [Seata Website](https://github.com/apache/incubator-seata.github.io) - Seata official website
* [Seata](https://github.com/apache/incubator-seata)- Seata client and server
* [Seata GoLang](https://github.com/apache/incubator-seata-go) - Seata GoLang client and server
* [Seata Samples](https://github.com/apache/incubator-seata-samples) - Samples for Seata
* [Seata GoLang Samples](https://github.com/apache/incubator-seata-go-samples) - Samples for Seata GoLang
* [Seata K8s](https://github.com/apache/incubator-seata-k8s) - Seata integration with k8s
* [Seata CLI](https://github.com/apache/incubator-seata-ctl) - CLI tool for Seata

## Contributors

This project exists thanks to all the people who contribute. [[Contributors](https://github.com/apache/incubator-seata-go/graphs/contributors)].

## License

Seata-go is under the Apache 2.0 license. See the [LICENSE](https://github.com/apache/incubator-seata-go/blob/master/LICENSE) file for details.
# seata-golang

### 一个朴素的想法
作为一个刚入 Golang 坑的微服务普通开发者来讲,很容易产生一个朴素的想法,希望 Golang 微服务也有分布式事务解决方案。我们注意到阿里开源了 Java 版的分布式事务解决方案 Seata,本项目尝试将 Java 版的 Seata 改写一个 Golang 的版本。
在 Seata 没有 Golang 版本 client sdk 的情况下,Golang 版本的 TC Server 使用了和 Java 版 Seata 一样的通信协议,方便调试。
目前,基于内存的 TC Server 已实现。希望有同样朴素想法的开发者加入我们一起完善 Golang 版本的分布式事务解决方案。

### todo list
- [X] Memory Session Manager
- [ ] DB Session Manager
- [ ] ETCD Session Manager
- [ ] TC
- [ ] RM

+ 0
- 164
README_ZH.md View File

@@ -1,164 +0,0 @@

<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div style="align: center">

<img src="https://img.alicdn.com/imgextra/i1/O1CN011z0JfQ2723QgDiWuH_!!6000000007738-2-tps-1497-401.png" height="100" width="426"/>

</div>
# Seata-go:简单可扩展的自主事务架构(Go 语言版本)

[![CI](https://github.com/apache/incubator-seata-go/actions/workflows/build.yml/badge.svg)](https://github.com/apache/incubator-seata-go/actions/workflows/build.yml) [![license](https://img.shields.io/github/license/apache/incubator-seata-go.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) [![codecov](https://codecov.io/gh/apache/incubator-seata-go/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/incubator-seata-go) [English](./README.md)


## 什么是 Seata-go?


Apache Seata(incubating)是一个非常成熟的分布式事务框架,是 Java 领域事实上的标准分布式事务平台。Seata-go 是其在多语言生态中 Go 语言的实现版本,实现了 Java 与 Go 之间的互操作,使得 Go 开发者也可以使用 seata-go 实现分布式事务。请访问 [Seata 官网](https://seata.apache.org/) 获取快速入门和文档。

Seata-go 的原理与 Seata-java 一致,由 TM、RM 和 TC 三部分组成。其中 TC 功能复用 Java 的实现,而 TM 和 RM 则已与 Seata-java 对接。

### 微服务中的分布式事务问题

假设我们有一个传统的单体应用,其业务由三个模块组成,使用一个本地数据源,自然由本地事务保障数据一致性。
![Monolithic App](https://img.alicdn.com/imgextra/i3/O1CN01FTtjyG1H4vvVh1sNY_!!6000000000705-0-tps-1106-678.jpg)
但在微服务架构中,这三个模块被设计成了三个不同的服务,分别使用不同的数据源([数据库每服务模式](http://microservices.io/patterns/data/database-per-service.html))。单个服务内的数据一致性可以通过本地事务保障。

但**整个业务范围的一致性如何保障?**
![Microservices Problem](https://img.alicdn.com/imgextra/i1/O1CN01DXkc3o1te9mnJcHOr_!!6000000005926-0-tps-1268-804.jpg)


### Seata-go 如何做?

Seata-go 就是为了解决上述问题而生。
![Seata solution](https://img.alicdn.com/imgextra/i1/O1CN01FheliH1k5VHIRob3p_!!6000000004632-0-tps-1534-908.jpg)
首先,如何定义一个**分布式事务**?
我们认为,分布式事务是一个**全局事务**,由一组**分支事务**组成,而**分支事务**通常就是**本地事务**。
![Global & Branch](https://cdn.nlark.com/lark/0/2018/png/18862/1545015454979-a18e16f6-ed41-44f1-9c7a-bd82c4d5ff99.png)


Seata-go 中有三个角色:

- **事务协调器(TC)**:维护全局和分支事务的状态,驱动全局提交或回滚。

- **事务管理器(TM)**:定义全局事务的范围,开始、提交或回滚全局事务。

- **资源管理器(RM)**:管理分支事务所处理的资源,与 TC 通信注册分支事务并报告状态,驱动分支事务提交或回滚。
![Model](https://cdn.nlark.com/lark/0/2018/png/18862/1545013915286-4a90f0df-5fda-41e1-91e0-2aa3d331c035.png)


Seata-go 分布式事务的典型生命周期如下:



1. TM 请求 TC 开启一个新的全局事务,TC 生成表示全局事务的 XID。

2. XID 在微服务调用链中传播。

3. RM 将本地事务注册为该 XID 对应的全局事务的分支事务。

4. TM 请求 TC 提交或回滚该 XID 对应的全局事务。

5. TC 驱动该 XID 对应的所有分支事务进行提交或回滚。

![Typical Process](https://cdn.nlark.com/lark/0/2018/png/18862/1545296917881-26fabeb9-71fa-4f3e-8a7a-fc317d3389f4.png)

更多原理和设计细节,请参阅 [Seata Wiki 页面](https://github.com/apache/incubator-seata/wiki)。



### 历史背景

##### 阿里巴巴

- **TXC**:淘宝事务构建器。2014 年阿里中间件团队启动,用于应对从单体架构转向微服务带来的分布式事务问题。

- **GTS**:全局事务服务。2016 年 TXC 在阿里云上线,更名为 GTS。

- **Fescar**:2019 年开始基于 TXC/GTS 启动开源项目 Fescar,与社区合作。

##### 蚂蚁金服

- **XTS**:扩展事务服务。自 2007 年开始开发的分布式事务中间件,被广泛应用于蚂蚁金服,解决跨库跨服务数据一致性问题。

- **DTX**:分布式事务扩展。2013 年起在蚂蚁金服云发布,命名为 DTX。
##### Seata 社区

- **Seata**:简单可扩展的自主事务架构。蚂蚁金服加入 Fescar,使其成为一个更加中立开放的社区,Fescar 更名为 Seata。

## 如何运行?
```go

go get seata.apache.org/seata-go@2.0.0

```
如果你想了解如何使用和集成 seata-go,请参考 [apache/seata-go-samples](https://github.com/apache/incubator-seata-go-samples)
## 如何查找最新版本
访问 Seata-Go 的 GitHub Releases 页面

打开:https://github.com/seata/seata-go/releases

最新的 tag / release 就是目前的最新稳定版本。
## 文档

你可以访问 Seata 官方网站获取完整文档:[Seata 官网](https://seata.apache.org/zh-cn/docs/overview/what-is-seata)
## 问题报告

若有问题,请遵循 [模板](./.github/ISSUE_TEMPLATE/BUG_REPORT_TEMPLATE.md) 报告问题。

## 安全

请勿使用公开的问题跟踪器,详情请参阅我们的 [安全政策](https://github.com/apache/incubator-seata/blob/2.x/SECURITY.md)

## 参与贡献
Seata-go 当前处于建设阶段,欢迎业界同仁加入我们,共同推进 Seata-go 建设!如果你想为 Seata-go 贡献代码,请参考 [代码贡献规范](./CONTRIBUTING_CN.md),也可以加入我们的社区钉钉群:33069364 一起交流!

## 联系方式

* 邮件列表:  

  * dev@seata.apache.org - 用于开发/用户讨论   [订阅](mailto:dev-subscribe@seata.apache.org)、[取消订阅](mailto:dev-unsubscribe@seata.apache.org)、[归档](https://lists.apache.org/list.html?dev@seata.apache.org)

* 在线交流:  

| 钉钉 | 微信公众号 | QQ | 微信助手 |
|:---------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------:|
| <img src="https://seata.apache.org/zh-cn/assets/images/dingtalk-group-67f42c9466fb2268b6927bb16b549d6c.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/wechat-official-467d10305f5449e6b2096e65d23a9d02.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/qq-group-8d8a89699cdb9ba8818364069475ba96.jpg" width="150" /> | <img src="https://seata.apache.org/zh-cn/assets/images/wechat-f8a87a96973942b826e32d1aed9bc8d9.jpg" width="150" /> |


## Seata 生态系统

* [Seata 官网](https://github.com/apache/incubator-seata.github.io)- Seata官方网站

* [Seata](https://github.com/apache/incubator-seata) - Seata 客户端和服务端

* [Seata GoLang](https://github.com/apache/incubator-seata-go) - Seata Go 客户端和服务端

* [Seata 示例](https://github.com/apache/incubator-seata-samples)- Seata 示例

* [Seata Go 示例](https://github.com/apache/incubator-seata-go-samples)- Seata GoLang 示例

* [Seata K8s 集成](https://github.com/apache/incubator-seata-k8s)- Seata 与 K8S 集成

* [Seata 命令行工具](https://github.com/apache/incubator-seata-ctl)- Seata CLI 工具

## 贡献者

Seata-go感谢所有贡献者的付出。[[贡献者列表](https://github.com/apache/incubator-seata-go/graphs/contributors)]
## 许可证

Seata-go 使用 Apache 2.0 协议,详情请查看 [LICENSE 文件](https://github.com/apache/incubator-seata-go/blob/master/LICENSE)

+ 0
- 78
changes/0.1.0.md View File

@@ -1,78 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 0.1.0-rc1

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 0.1.0-rc1

Seata-go 0.1.0-rc1 Released.

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:

- [[#1](https://github.com/apache/incubator-seata-go/commit/06b9969bb3fd24071adc271dc543c3eb684070c9)] initialize project structure and support tcc local
- [[#2](https://github.com/apache/incubator-seata-go/commit/80913fa73e38fd3c159dcd28804344b9a87f718c)] add github Actions
- [[#122](https://github.com/apache/incubator-seata-go/pull/122)] feat: add two phase and tcc dubbo
- [[#127](https://github.com/apache/incubator-seata-go/pull/127)] feat: transaction at datasource

### bugfix:

- [[#5](https://github.com/apache/incubator-seata-go/commit/48f1b6bf6c8890d649ceac3d048f61695dce2f7a)] fix cli bug
- [[#15](https://github.com/apache/incubator-seata-go/commit/de615531e9d17af66067c54452ee5bce2d670008)] fix branch commit bug
- [[#34](https://github.com/apache/incubator-seata-go/commit/846a3b336194f9d188f07bf6af65f617b0baf489)] style:change bool to struct{}
- [[#130](https://github.com/apache/incubator-seata-go/pull/130)] fix: getty session auto close bug
- [[#155](https://github.com/apache/incubator-seata-go/pull/155)] bugfix: fix rollback response status bug

### optimize:

- [[#3](https://github.com/apache/incubator-seata-go/commit/65c2e1ed676a2306eb10f7d43e3bf5b37271ee3e)] adjust the structure of the project
- [[#18](https://github.com/apache/incubator-seata-go/commit/de615531e9d17af66067c54452ee5bce2d670008)] remove goetty
- [[#19](https://github.com/apache/incubator-seata-go/commit/de615531e9d17af66067c54452ee5bce2d670008)] optimize codec code
- [[#125](https://github.com/apache/incubator-seata-go/pull/125)] optimize named for the resource manager api
- [[#165](https://github.com/apache/incubator-seata-go/pull/165)] test: add unit test and labeler workflow

### test:

- [[#9f4d8](https://github.com/apache/incubator-seata-go/commit/9f4d8cc0b6f1e26860cded5ab05b504ad6a6d6ff)] add unit test for codec

### doc:

- [[#0](https://github.com/apache/incubator-seata-go/commit/fcda132629032321a7cc733a7a2ed02e05c2151b)] hello world
- [[#146](https://github.com/apache/incubator-seata-go/pull/146)] doc: add license
- [[#153](https://github.com/apache/incubator-seata-go/pull/153)] docs: add readme ,contributing and pr template doc
- [[#167](https://github.com/apache/incubator-seata-go/pull/167)] fix typo in reademe

### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [AlexStocks](https://github.com/AlexStocks)
- [luky116](https://github.com/luky116)
- [106umao](https://github.com/106umao)
- [liiibpm](https://github.com/liiibpm)
- [cgDeepLearn](https://github.com/cgDeepLearn)
- [Penglq](https://github.com/Penglq)

Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.

</detail>

+ 0
- 78
changes/0.1.0_zh.md View File

@@ -1,78 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 0.1.0-rc1

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 0.1.0-rc1

Seata-go 0.1.0-rc1 发布。

Seata-go 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

此版本更新如下:

### feature:

- [[#1](https://github.com/apache/incubator-seata-go/commit/06b9969bb3fd24071adc271dc543c3eb684070c9)] 项目初始化,并且支持TCC本地模式
- [[#2](https://github.com/apache/incubator-seata-go/commit/80913fa73e38fd3c159dcd28804344b9a87f718c)] 添加 github 工作流
- [[#122](https://github.com/apache/incubator-seata-go/pull/122)] 添加二阶段提交和TCC dubbo调用
- [[#127](https://github.com/apache/incubator-seata-go/pull/127)] 添加事务数据源代理

### bugfix:

- [[#5](https://github.com/apache/incubator-seata-go/commit/48f1b6bf6c8890d649ceac3d048f61695dce2f7a)] 修复cli的问题
- [[#15](https://github.com/apache/incubator-seata-go/commit/de615531e9d17af66067c54452ee5bce2d670008)] 修复提交分支事务的问题
- [[#34](https://github.com/apache/incubator-seata-go/commit/846a3b336194f9d188f07bf6af65f617b0baf489)] 将bool改为struct{}
- [[#130](https://github.com/apache/incubator-seata-go/pull/130)] 修复getty 自动关闭session的问题
- [[#155](https://github.com/apache/incubator-seata-go/pull/155)] 修复分支事务回滚时返回值status的问题

### optimize:

- [[#3](https://github.com/apache/incubator-seata-go/commit/65c2e1ed676a2306eb10f7d43e3bf5b37271ee3e)] 调整项目结构
- [[#18](https://github.com/apache/incubator-seata-go/commit/de615531e9d17af66067c54452ee5bce2d670008)] 移除 goetty 包依赖
- [[#19](https://github.com/apache/incubator-seata-go/commit/de615531e9d17af66067c54452ee5bce2d670008)] 重构 codec 的代码
- [[#125](https://github.com/apache/incubator-seata-go/pull/125)] 重名 rm api 的接口
- [[#165](https://github.com/apache/incubator-seata-go/pull/165)] 完善 github 工作流

### test:

- [[#9f4d8](https://github.com/apache/incubator-seata-go/commit/9f4d8cc0b6f1e26860cded5ab05b504ad6a6d6ff)] 添加codec的单测

### doc:

- [[#0](https://github.com/apache/incubator-seata-go/commit/fcda132629032321a7cc733a7a2ed02e05c2151b)] hello world
- [[#146](https://github.com/apache/incubator-seata-go/pull/146)] 添加 license 文件
- [[#153](https://github.com/apache/incubator-seata-go/pull/153)] 添加 readme 、contributing 和 pr template 文件
- [[#167](https://github.com/apache/incubator-seata-go/pull/167)] 完善 reamdme 文件格式

### contributors:

非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

- [AlexStocks](https://github.com/AlexStocks)
- [luky116](https://github.com/luky116)
- [106umao](https://github.com/106umao)
- [liiibpm](https://github.com/liiibpm)
- [cgDeepLearn](https://github.com/cgDeepLearn)
- [Penglq](https://github.com/Penglq)

同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

</detail>

+ 0
- 156
changes/1.0.2-RC1.md View File

@@ -1,156 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.0.2-RC1

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.0.2-RC1

Seata-go 1.0.2-RC1 Released.

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:
- [[#190](https://github.com/apache/incubator-seata-go/pull/190)] add TCC branch report
- [[#158](https://github.com/apache/incubator-seata-go/pull/158)] TCC mode supports grpc call
- [[#213](https://github.com/apache/incubator-seata-go/pull/213)] support data source proxy function
- [[#240](https://github.com/apache/incubator-seata-go/pull/240)] add undo log manager delete
- [[#243](https://github.com/apache/incubator-seata-go/pull/243)] add update sql parser
- [[#191](https://github.com/apache/incubator-seata-go/pull/191)] add fence for TCC, and add fence sample in TCC local mode
- [[#264](https://github.com/apache/incubator-seata-go/pull/264)] add update sql parser and remove tidb parser
- [[#280](https://github.com/apache/incubator-seata-go/pull/280)] TCC supports http calling
- [[#245](https://github.com/apache/incubator-seata-go/pull/245)] support hasLogTable logic
- [[#288](https://github.com/apache/incubator-seata-go/pull/288)] add MySQL update SQL undo log builder
- [[#296](https://github.com/apache/incubator-seata-go/pull/296)] add MySQL delete SQL undo log builder
- [[#303](https://github.com/apache/incubator-seata-go/pull/303)] add sync worker
- [[#289](https://github.com/apache/incubator-seata-go/pull/289)] add MySQL update SQL after undo log builder
- [[#294](https://github.com/apache/incubator-seata-go/pull/294)] add MySQL table meta query
- [[#309](https://github.com/apache/incubator-seata-go/pull/309)] init compressor type
- [[#301](https://github.com/apache/incubator-seata-go/pull/301)] add MySQL multi SQL undo log builder
- [[#321](https://github.com/apache/incubator-seata-go/pull/321)] add deflate compress
- [[#324](https://github.com/apache/incubator-seata-go/pull/324)] add LZ4 compressor
- [[#327](https://github.com/apache/incubator-seata-go/pull/327)] add zstd compressor
- [[#322](https://github.com/apache/incubator-seata-go/pull/322)] add gzip compressor
- [[#307](https://github.com/apache/incubator-seata-go/pull/307)] add flush undo log
- [[#329](https://github.com/apache/incubator-seata-go/pull/329)] add zip compressor
- [[#325](https://github.com/apache/incubator-seata-go/pull/325)] add MySQL multi update SQL undo log builder
- [[#330](https://github.com/apache/incubator-seata-go/pull/330)] add MySQL multi delete SQL undo log builder
- [[#319](https://github.com/apache/incubator-seata-go/pull/319)] add select for update
- [[#320](https://github.com/apache/incubator-seata-go/pull/320)] add undo logic
- [[#337](https://github.com/apache/incubator-seata-go/pull/337)] add insert undo log
- [[#355](https://github.com/apache/incubator-seata-go/pull/355)] support judging the number of undo log storage fields according to the configuration
- [[#365](https://github.com/apache/incubator-seata-go/pull/365)] do dirty data check before rolling back AT


### bugfix:

- [[#176](https://github.com/apache/incubator-seata-go/pull/176)] fix unit test bug of message
- [[#237](https://github.com/apache/incubator-seata-go/pull/237)] fix the bug of registering resources when executing the OpenConnector function
- [[#230](https://github.com/apache/incubator-seata-go/pull/230)] fix the bug of remote asynchronous call infinite loop
- [[#258](https://github.com/apache/incubator-seata-go/pull/258)] fix global transation time out bug
- [[#263](https://github.com/apache/incubator-seata-go/pull/263)] fix mock bug
- [[#326](https://github.com/apache/incubator-seata-go/pull/326)] fix fanout test data race
- [[#350](https://github.com/apache/incubator-seata-go/pull/350)] fix panic bug
- [[#359](https://github.com/apache/incubator-seata-go/pull/359)] fix insert undo log bug
- [[#368](https://github.com/apache/incubator-seata-go/pull/368)] fix AT rollback sample bug
- [[#363](https://github.com/apache/incubator-seata-go/pull/363)] fix meta data bug
- [[#365](https://github.com/apache/incubator-seata-go/pull/365)] fix decode undo log bug



### optimize:


- [[#187](https://github.com/apache/incubator-seata-go/pull/187)] optimize way of init seata-go
- [[#196](https://github.com/apache/incubator-seata-go/pull/196)] optimize remoting method's params
- [[#200](https://github.com/apache/incubator-seata-go/pull/200)] add TCC grpc sample and optimize register resource and branch register
- [[#208](https://github.com/apache/incubator-seata-go/pull/208)] optimize remove unnecessary codes
- [[#202](https://github.com/apache/incubator-seata-go/pull/202)] optimize workflow, add condecov and issue, stale robot
- [[#215](https://github.com/apache/incubator-seata-go/pull/215)] optimize the time parameter to improve readability
- [[#179](https://github.com/apache/incubator-seata-go/pull/179)] support instance BusinessActionContext outside the TCC try method
- [[#198](https://github.com/apache/incubator-seata-go/pull/198)] optimize function's parameters into one struct-rm_api.go
- [[#235](https://github.com/apache/incubator-seata-go/pull/235)] adjust MessageType enumeration value naming convention
- [[#238](https://github.com/apache/incubator-seata-go/pull/238)] add some todo comment, add a undo hook sample
- [[#229](https://github.com/apache/incubator-seata-go/pull/229)] add unit testing for common
- [[#261](https://github.com/apache/incubator-seata-go/pull/261)] optimize nested loop retries
- [[#284](https://github.com/apache/incubator-seata-go/pull/284)] optimize retry logic
- [[#286](https://github.com/apache/incubator-seata-go/pull/286)] separate the initialization logic of tm and rm
- [[#287](https://github.com/apache/incubator-seata-go/pull/287)] fefactor seata conn logic
- [[#281](https://github.com/apache/incubator-seata-go/pull/281)] optimize global transaction usage
- [[#295](https://github.com/apache/incubator-seata-go/pull/295)] fefactor seata conn logic
- [[#302](https://github.com/apache/incubator-seata-go/pull/302)] update dubbo-go version
- [[#336](https://github.com/apache/incubator-seata-go/pull/336)] optimize at overall process
- [[#346](https://github.com/apache/incubator-seata-go/pull/346)] optimize AT commit transaction process
- [[#352](https://github.com/apache/incubator-seata-go/pull/352)] optimize get meta data
- [[#354](https://github.com/apache/incubator-seata-go/pull/354)] optimize AT commit transaction process
- [[#353](https://github.com/apache/incubator-seata-go/pull/353)] modify some receiver name
- [[#356](https://github.com/apache/incubator-seata-go/pull/356)] optimize AT rollback transaction process


### test:

- [[#154](https://github.com/apache/incubator-seata-go/pull/154)] add unit test for message
- [[#163](https://github.com/apache/incubator-seata-go/pull/163)] add unit test for tm
- [[#203](https://github.com/apache/incubator-seata-go/pull/203)] add unit test for getty
- [[#204](https://github.com/apache/incubator-seata-go/pull/204)] add unit test for dubbo transtation filter
- [[#210](https://github.com/apache/incubator-seata-go/pull/210)] add unit test for Tcc branch report
- [[#192](https://github.com/apache/incubator-seata-go/pull/192)] add unit test for rm
- [[#229](https://github.com/apache/incubator-seata-go/pull/229)] add unit test for common
- [[#299](https://github.com/apache/incubator-seata-go/pull/299)] add unit test for SQL Parser
- [[#332](https://github.com/apache/incubator-seata-go/pull/332)] add unit test for multi delete undo log
- [[#358](https://github.com/apache/incubator-seata-go/pull/358)] add AT rollback sample


### doc:

- [[#202](https://github.com/apache/incubator-seata-go/pull/202)] optimize workflow, add condecov and issue
- [[#254](https://github.com/apache/incubator-seata-go/pull/254)] add license automatic check script
- [[#305](https://github.com/apache/incubator-seata-go/pull/305)] config github action not to automatically close pr or issue


### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [AlexStocks](https://github.com/AlexStocks)
- [luky116](https://github.com/luky116)
- [106umao](https://github.com/106umao)
- [liiibpm](https://github.com/liiibpm)
- [elrond-g](https://github.com/elrond-g)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/apache/incubator-seata-go/commits?author=iSuperCoder)
- [a631807682](https://github.com/apache/incubator-seata-go/commits?author=a631807682)
- [betterwinsone](https://github.com/apache/incubator-seata-go/commits?author=betterwinsone)
- [jasondeng1997](https://github.com/apache/incubator-seata-go/commits?author=jasondeng1997)
- [chuntaojun](https://github.com/apache/incubator-seata-go/commits?author=chuntaojun)
- [complone](https://github.com/apache/incubator-seata-go/commits?author=complone)
- [miaoxueyu](https://github.com/apache/incubator-seata-go/commits?author=miaoxueyu)
- [PangXing](https://github.com/apache/incubator-seata-go/commits?author=PangXing)
- [georgehao](https://github.com/apache/incubator-seata-go/commits?author=georgehao)
- [baerwang](https://github.com/apache/incubator-seata-go/commits?author=baerwang)
- [raspberry-hu](https://github.com/apache/incubator-seata-go/commits?author=raspberry-hu)
- [WyattJia](https://github.com/apache/incubator-seata-go/commits?author=WyattJia)
- [Code-Fight](https://github.com/Code-Fight)
- [betterwinsone](https://github.com/betterwinsonet)

Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.

</detail>

+ 0
- 157
changes/1.0.2-RC1_zh.md View File

@@ -1,157 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.0.2-RC1

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.0.2-RC1

Seata-go 1.0.2-RC1 发布。

Seata-go 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

此版本更新如下:

### feature:

- [[#190](https://github.com/apache/incubator-seata-go/pull/190)] 添加分支状态上报接口
- [[#158](https://github.com/apache/incubator-seata-go/pull/158)] TCC 模式支持 grapc 调用
- [[#213](https://github.com/apache/incubator-seata-go/pull/213)] 支持数据源代理功能
- [[#240](https://github.com/apache/incubator-seata-go/pull/240)] 删除日志管理功能
- [[#243](https://github.com/apache/incubator-seata-go/pull/243)] 添加 Update SQL 语法解析器
- [[#191](https://github.com/apache/incubator-seata-go/pull/191)] 支持 TCC 防悬挂、空回滚处理功能
- [[#264](https://github.com/apache/incubator-seata-go/pull/264)] 添加更新sql解析器并删除 tidb 解析器
- [[#280](https://github.com/apache/incubator-seata-go/pull/280)] TCC 支持 http 调用
- [[#245](https://github.com/apache/incubator-seata-go/pull/245)] 支持 hasLogTable 判断逻辑
- [[#288](https://github.com/apache/incubator-seata-go/pull/288)] 添加 update SQL 的 undo log 生成功能
- [[#296](https://github.com/apache/incubator-seata-go/pull/296)] 添加 delete SQL 的 undo log 生成功能
- [[#303](https://github.com/apache/incubator-seata-go/pull/303)] 添加异步处理器
- [[#289](https://github.com/apache/incubator-seata-go/pull/289)] 撤消日志生成器后添加 MySQL 更新
- [[#294](https://github.com/apache/incubator-seata-go/pull/294)] 添加 MySQL 元数据查询
- [[#309](https://github.com/apache/incubator-seata-go/pull/309)] 初始化压缩类型
- [[#301](https://github.com/apache/incubator-seata-go/pull/301)] 添加 Multi SQL 的 undo log 生成功能
- [[#321](https://github.com/apache/incubator-seata-go/pull/321)] 添加 deflate 压缩功能
- [[#324](https://github.com/apache/incubator-seata-go/pull/324)] 添加 lz4 压缩功能
- [[#327](https://github.com/apache/incubator-seata-go/pull/327)] 添加 zstd 压缩功能
- [[#322](https://github.com/apache/incubator-seata-go/pull/322)] 添加 gzip 压缩功能
- [[#307](https://github.com/apache/incubator-seata-go/pull/307)] 添加 flush undo log 功能
- [[#329](https://github.com/apache/incubator-seata-go/pull/329)] 添加 zip 压缩功能
- [[#325](https://github.com/apache/incubator-seata-go/pull/325)] 添加 Multi update SQL 的 undo log 生成功能
- [[#330](https://github.com/apache/incubator-seata-go/pull/330)] 添加 Multi delete SQL 的 undo log 生成功能
- [[#319](https://github.com/apache/incubator-seata-go/pull/319)] 添加选择更新执行器
- [[#320](https://github.com/apache/incubator-seata-go/pull/320)] 添加 undo 逻辑
- [[#337](https://github.com/apache/incubator-seata-go/pull/337)] 添加插入 undo log 逻辑
- [[#355](https://github.com/apache/incubator-seata-go/pull/355)] 支持根据配置判断 undo log 保存字段个数
- [[#365](https://github.com/apache/incubator-seata-go/pull/365)] 回滚 AT 之前做脏数据校验



### bugfix:

- [[#176](https://github.com/apache/incubator-seata-go/pull/176)] 修复 message 的单测的 bug
- [[#237](https://github.com/apache/incubator-seata-go/pull/237)] 修复在执行 OpenConnector 函数时候注册资源的 bug
- [[#230](https://github.com/apache/incubator-seata-go/pull/230)] 修复远程异步调用无限循环的bug
- [[#258](https://github.com/apache/incubator-seata-go/pull/258)] 修复全局事务超时的 bug
- [[#263](https://github.com/apache/incubator-seata-go/pull/263)] 修复 mock 数据的 bug
- [[#326](https://github.com/apache/incubator-seata-go/pull/326)] 修复 fanout 单元测试 bug
- [[#350](https://github.com/apache/incubator-seata-go/pull/350)] 修复 panic 的bug
- [[#359](https://github.com/apache/incubator-seata-go/pull/359)] 修复插入 undo log 的 MySQL 参数 bug
- [[#360](https://github.com/apache/incubator-seata-go/pull/360)] 修复 AT 回滚例子 bug
- [[#363](https://github.com/apache/incubator-seata-go/pull/363)] 修复 meta data bug
- [[#365](https://github.com/apache/incubator-seata-go/pull/365)] 修复反序列化 undo log bug


### optimize:

- [[#187](https://github.com/apache/incubator-seata-go/pull/187)] 优化 seata-go 初始化流程
- [[#196](https://github.com/apache/incubator-seata-go/pull/196)] 优化远程调用方法参数
- [[#200](https://github.com/apache/incubator-seata-go/pull/200)] 添加 tcc grpc 样例,优化注册资源和分支注册
- [[#208](https://github.com/apache/incubator-seata-go/pull/208)] 优化删除不必要代码
- [[#215](https://github.com/apache/incubator-seata-go/pull/215)] 优化使时间参数,提高可读性
- [[#179](https://github.com/apache/incubator-seata-go/pull/179)] 支持 TCC 一阶段传入用户自定义参数
- [[#198](https://github.com/apache/incubator-seata-go/pull/198)] 优化远程调用方法的传参
- [[#235](https://github.com/apache/incubator-seata-go/pull/235)] 调整 MessageType 枚举值命名规范
- [[#238](https://github.com/apache/incubator-seata-go/pull/238)] 添加一些待办事项注释,添加 hook 例子
- [[#261](https://github.com/apache/incubator-seata-go/pull/261)] 优化嵌套循环重试
- [[#284](https://github.com/apache/incubator-seata-go/pull/284)] 优化重试逻辑
- [[#286](https://github.com/apache/incubator-seata-go/pull/286)] 将 rm 和 tm 的初始化逻辑拆分
- [[#287](https://github.com/apache/incubator-seata-go/pull/287)] 重构 seata conn 逻辑
- [[#281](https://github.com/apache/incubator-seata-go/pull/281)] 优化全局事务使用
- [[#295](https://github.com/apache/incubator-seata-go/pull/295)] 重构 seata conn 逻辑
- [[#302](https://github.com/apache/incubator-seata-go/pull/302)] 修改 dubbo-go 版本
- [[#336](https://github.com/apache/incubator-seata-go/pull/336)] 优化 at 整体流程
- [[#346](https://github.com/apache/incubator-seata-go/pull/346)] 优化 at 事务提交流程
- [[#352](https://github.com/apache/incubator-seata-go/pull/352)] 优化获取元数据流程
- [[#354](https://github.com/apache/incubator-seata-go/pull/354)] 优化 at 事务提交流程
- [[#353](https://github.com/apache/incubator-seata-go/pull/353)] 修改方法接收者命名规范
- [[#356](https://github.com/apache/incubator-seata-go/pull/356)] 优化 at 事务回滚流程

### test:

- [[#154](https://github.com/apache/incubator-seata-go/pull/154)] 添加 message 单元单测
- [[#163](https://github.com/apache/incubator-seata-go/pull/163)] 添加 tm 单元单测
- [[#203](https://github.com/apache/incubator-seata-go/pull/203)] 添加 getty 单元测试
- [[#204](https://github.com/apache/incubator-seata-go/pull/204)] 添加 dubbo filter 单元测试
- [[#210](https://github.com/apache/incubator-seata-go/pull/210)] 添加 Tcc 分支报告测试
- [[#192](https://github.com/apache/incubator-seata-go/pull/192)] 添加 rm 单元测试
- [[#229](https://github.com/apache/incubator-seata-go/pull/229)] 添加 common 单元测试
- [[#299](https://github.com/apache/incubator-seata-go/pull/299)] 添加 SQL Parser 单元测试
- [[#332](https://github.com/apache/incubator-seata-go/pull/332)] 添加 multi delete undo log 单元测试
- [[#358](https://github.com/apache/incubator-seata-go/pull/358)] 添加 AT 回滚的例子


### doc:

- [[#202](https://github.com/apache/incubator-seata-go/pull/202)] 优化 github CI 流程,添加 condecov 、 issue CI 工作流
- [[#254](https://github.com/apache/incubator-seata-go/pull/254)] 添加自动检查 licence 的脚本
- [[#305](https://github.com/apache/incubator-seata-go/pull/305)] 修改 CI 不要自动关闭 issue 和 pr




### contributors:

非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

- [AlexStocks](https://github.com/AlexStocks)
- [luky116](https://github.com/luky116)
- [106umao](https://github.com/106umao)
- [liiibpm](https://github.com/liiibpm)
- [elrond-g](https://github.com/elrond-g)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/apache/incubator-seata-go/commits?author=iSuperCoder)
- [a631807682](https://github.com/apache/incubator-seata-go/commits?author=a631807682)
- [betterwinsone](https://github.com/apache/incubator-seata-go/commits?author=betterwinsone)
- [jasondeng1997](https://github.com/apache/incubator-seata-go/commits?author=jasondeng1997)
- [chuntaojun](https://github.com/apache/incubator-seata-go/commits?author=chuntaojun)
- [complone](https://github.com/apache/incubator-seata-go/commits?author=complone)
- [miaoxueyu](https://github.com/apache/incubator-seata-go/commits?author=miaoxueyu)
- [PangXing](https://github.com/apache/incubator-seata-go/commits?author=PangXing)
- [georgehao](https://github.com/apache/incubator-seata-go/commits?author=georgehao)
- [baerwang](https://github.com/apache/incubator-seata-go/commits?author=baerwang)
- [raspberry-hu](https://github.com/apache/incubator-seata-go/commits?author=raspberry-hu)
- [WyattJia](https://github.com/apache/incubator-seata-go/commits?author=WyattJia)
- [Code-Fight](https://github.com/Code-Fight)
- [betterwinsone](https://github.com/betterwinsonet)


同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

</detail>


+ 0
- 95
changes/1.0.3.md View File

@@ -1,95 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.0.3

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.0.3

Seata-go 1.0.3 Released.

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:

- [[#380](https://github.com/apache/incubator-seata-go/pull/380)] support xa mysql connection
- [[#383](https://github.com/apache/incubator-seata-go/pull/383)] support read tcc fence configuration file
- [[#389](https://github.com/apache/incubator-seata-go/pull/389)] add the transaction id of xa mode
- [[#398](https://github.com/apache/incubator-seata-go/pull/398)] support read TM configuration file
- [[#399](https://github.com/apache/incubator-seata-go/pull/399)] support read getty configuration file
- [[#405](https://github.com/apache/incubator-seata-go/pull/405)] support at mode insert on duplicate sql parsing
- [[#406](https://github.com/apache/incubator-seata-go/pull/406)] support read transport configuration file
- [[#410](https://github.com/apache/incubator-seata-go/pull/410)] support read undo log configuration file
- [[#411](https://github.com/apache/incubator-seata-go/pull/411)] use tm's profile properties in the project
- [[#412](https://github.com/apache/incubator-seata-go/pull/412)] support read rm configuration file
- [[#412](https://github.com/apache/incubator-seata-go/pull/412)] support read service configuration file
- [[#419](https://github.com/apache/incubator-seata-go/pull/419)] use undo-log's profile properties in the project

### bugfix:

- [[#387](https://github.com/apache/incubator-seata-go/pull/387)] fix loop recursion problem in OpenConnector
- [[#401](https://github.com/apache/incubator-seata-go/pull/401)] fix branch register process
- [[#418](https://github.com/apache/incubator-seata-go/pull/418)] fix the configuration file problem of undo log
- [[#423](https://github.com/apache/incubator-seata-go/pull/423)] fix getty initialization failure
- [[#424](https://github.com/apache/incubator-seata-go/pull/424)] fix getty initialization failure
- [[#429](https://github.com/apache/incubator-seata-go/pull/429)] fix the problem of execution failure in at mode

### optimize:

- [[#366](https://github.com/apache/incubator-seata-go/pull/366)] add data check before rollbeck
- [[#367](https://github.com/apache/incubator-seata-go/pull/367)] simplify to make codes more readable
- [[#369](https://github.com/apache/incubator-seata-go/pull/369)] remove unless function
- [[#385](https://github.com/apache/incubator-seata-go/pull/385)] optimize the SQL used in AT sample
- [[#388](https://github.com/apache/incubator-seata-go/pull/388)] optimize comments and dead code
- [[#390](https://github.com/apache/incubator-seata-go/pull/390)] optime rm init
- [[#392](https://github.com/apache/incubator-seata-go/pull/392)] optimize code style
- [[#394](https://github.com/apache/incubator-seata-go/pull/394)] optimize at mode base executor
- [[#400](https://github.com/apache/incubator-seata-go/pull/400)] optime protocol init
- [[#408](https://github.com/apache/incubator-seata-go/pull/408)] optime log init
- [[#409](https://github.com/apache/incubator-seata-go/pull/409)] refactor logic of delete and insert sql in at mode
- [[#414](https://github.com/apache/incubator-seata-go/pull/414)] rename unit test file
- [[#422](https://github.com/apache/incubator-seata-go/pull/422)] remove unused config code

### test:

### doc:
- [[#417](https://github.com/apache/incubator-seata-go/pull/417)] optiomize readme file

### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [AlexStocks](https://github.com/AlexStocks)
- [luky116](https://github.com/luky116)
- [georgehao](https://github.com/georgehao)
- [lxfeng1997](https://github.com/lxfeng1997)
- [106umao](https://github.com/106umao)
- [liiibpm](https://github.com/liiibpm)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/iSuperCoder)
- [jasondeng1997](https://github.com/jasondeng1997)
- [Charlie17Li](https://github.com/Charlie17Li)
- [Code-Fight](https://github.com/Code-Fight)
- [Kirhaku](https://github.com/Kirhaku)

Also, we receive many valuable issues, questions and advices from our community. Thanks all.

</detail>

+ 0
- 98
changes/1.0.3_zh.md View File

@@ -1,98 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.0.3

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.0.3

Seata-go 1.0.3 发布。

Seata-go 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

此版本更新如下:

### feature:

- [[#380](https://github.com/apache/incubator-seata-go/pull/380)] 支持 MySQL XA 的连接
- [[#383](https://github.com/apache/incubator-seata-go/pull/383)] 支持 TCC Fence 读取配置文件
- [[#389](https://github.com/apache/incubator-seata-go/pull/389)] 添加 XA 模式的事务ID
- [[#398](https://github.com/apache/incubator-seata-go/pull/398)] 支持 TM 读取配置文件
- [[#399](https://github.com/apache/incubator-seata-go/pull/399)] 支持 getty 读取配置文件
- [[#405](https://github.com/apache/incubator-seata-go/pull/405)] 支持 AT 模式 insert on duplicate SQL 解析
- [[#406](https://github.com/apache/incubator-seata-go/pull/406)] 支持 transport 读取配置文件
- [[#410](https://github.com/apache/incubator-seata-go/pull/410)] 支持 undo log 读取配置文件
- [[#411](https://github.com/apache/incubator-seata-go/pull/411)] 在项目中使用 tm 的配置文件属性
- [[#412](https://github.com/apache/incubator-seata-go/pull/412)] 支持 RM 读取配置文件
- [[#413](https://github.com/apache/incubator-seata-go/pull/413)] 支持 service 读取配置文件
- [[#419](https://github.com/apache/incubator-seata-go/pull/419)] 在项目中使用 undo log 的配置文件属性
- [[#421](https://github.com/apache/incubator-seata-go/pull/421)] 支持 service 读取配置文件

### bugfix:

- [[#387](https://github.com/apache/incubator-seata-go/pull/387)] 修复 OpenConnector 中死循环的问题
- [[#401](https://github.com/apache/incubator-seata-go/pull/401)] 优化注册事务分支的流程
- [[#418](https://github.com/apache/incubator-seata-go/pull/418)] 修复 undo log 的配置文件的问题
- [[#423](https://github.com/apache/incubator-seata-go/pull/423)] 修复 getty 初始化失败的问题
- [[#424](https://github.com/apache/incubator-seata-go/pull/424)] 修复 getty 初始化失败的问题
- [[#429](https://github.com/apache/incubator-seata-go/pull/429)] 修复 AT 模式执行失败的问题

### optimize:

- [[#366](https://github.com/apache/incubator-seata-go/pull/366)] AT 回滚前添加数据校验逻辑
- [[#367](https://github.com/apache/incubator-seata-go/pull/367)] 优化 AT 代码的命名
- [[#369](https://github.com/apache/incubator-seata-go/pull/369)] 移除不用的方法
- [[#385](https://github.com/apache/incubator-seata-go/pull/385)] 优化 AT sample 的建表SQL
- [[#388](https://github.com/apache/incubator-seata-go/pull/388)] 优化代码注释,删除不用的代码
- [[#390](https://github.com/apache/incubator-seata-go/pull/390)] 优化 RM 的初始化流程
- [[#392](https://github.com/apache/incubator-seata-go/pull/392)] 优化代码的风格问题
- [[#394](https://github.com/apache/incubator-seata-go/pull/394)] 重构 AT 模式的执行器
- [[#400](https://github.com/apache/incubator-seata-go/pull/400)] 优化 protocol 的初始化流程
- [[#408](https://github.com/apache/incubator-seata-go/pull/408)] 优化 log 的初始化流程
- [[#409](https://github.com/apache/incubator-seata-go/pull/409)] 重构 AT 模式的 delete 和 insert SQL 的执行逻辑
- [[#414](https://github.com/apache/incubator-seata-go/pull/414)] 重命名单测文件
- [[#422](https://github.com/apache/incubator-seata-go/pull/422)] 移除未使用的 config 代码

### test:

### doc:
- [[#417](https://github.com/apache/incubator-seata-go/pull/417)] 调整 readme 文件内容


### contributors:

非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

- [AlexStocks](https://github.com/AlexStocks)
- [luky116](https://github.com/luky116)
- [georgehao](https://github.com/georgehao)
- [lxfeng1997](https://github.com/lxfeng1997)
- [106umao](https://github.com/106umao)
- [liiibpm](https://github.com/liiibpm)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/iSuperCoder)
- [jasondeng1997](https://github.com/jasondeng1997)
- [Charlie17Li](https://github.com/Charlie17Li)
- [Code-Fight](https://github.com/Code-Fight)
- [Kirhaku](https://github.com/Kirhaku)

同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

</detail>


+ 0
- 91
changes/1.1.0.md View File

@@ -1,91 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.1.0

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.1.0

Seata-go 1.1.0 Released.

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:

- [[#491](https://github.com/apache/incubator-seata-go/pull/491)] support query global lock key
- [[#482](https://github.com/apache/incubator-seata-go/pull/482)] support multi delete SQL executor in AT
- [[#481](https://github.com/apache/incubator-seata-go/pull/481)] support multi update SQL executor in AT
- [[#478](https://github.com/apache/incubator-seata-go/pull/478)] support select for update SQL executor in AT
- [[#477](https://github.com/apache/incubator-seata-go/pull/477)] support the json serialization method of undo log
- [[#456](https://github.com/apache/incubator-seata-go/pull/456)] support insert on update SQL executor in AT
- [[#444](https://github.com/apache/incubator-seata-go/pull/444)] support BZip2Compressor
- [[#436](https://github.com/apache/incubator-seata-go/pull/436)] use rm config file
- [[#433](https://github.com/apache/incubator-seata-go/pull/433)] support xa connect manager
- [[#430](https://github.com/apache/incubator-seata-go/pull/430)] use getty config file

### bugfix:

- [[#509](https://github.com/apache/incubator-seata-go/pull/509)] fix undo log SQLType when execute insert on update SQL in AT
- [[#495](https://github.com/apache/incubator-seata-go/pull/495)] fix undo log SQLType bug
- [[#487](https://github.com/apache/incubator-seata-go/pull/487)] fix at bug when execute
- [[#472](https://github.com/apache/incubator-seata-go/pull/472)] fix missing value of context When using global transactions
- [[#461](https://github.com/apache/incubator-seata-go/pull/461)] fix the problem of error_code_test
- [[#459](https://github.com/apache/incubator-seata-go/pull/459)] fix the rollback error log
- [[#452](https://github.com/apache/incubator-seata-go/pull/452)] fix the error of id self-increment when executing insert sql in AT

### optimize:

- [[#507](https://github.com/apache/incubator-seata-go/pull/507)] refactor logic of multiple update sql in AT
- [[#505](https://github.com/apache/incubator-seata-go/pull/505)] optimize multi SQL executor in AT
- [[#453](https://github.com/apache/incubator-seata-go/pull/453)] optimize the messageType and transactionErrorCode enum
- [[#447](https://github.com/apache/incubator-seata-go/pull/447)] optimize the datasource init process
- [[#466](https://github.com/apache/incubator-seata-go/pull/466)] optimize variable naming

### test:

- [[#445](https://github.com/apache/incubator-seata-go/pull/445)] add unit test for TransactionErrorCode and MessageType

### doc:

- [[#492](https://github.com/apache/incubator-seata-go/pull/492)] update feature list of readme
- [[#489](https://github.com/apache/incubator-seata-go/pull/489)] add change-log of version 1.1.0

### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [luky116](https://github.com/luky116)
- [georgehao](https://github.com/georgehao)
- [lxfeng1997](https://github.com/lxfeng1997)
- [106umao](https://github.com/106umao)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/iSuperCoder)
- [Charlie17Li](https://github.com/Charlie17Li)
- [Code-Fight](https://github.com/Code-Fight)
- [Kirhaku](https://github.com/Kirhaku)
- [Vaderkai](https://github.com/VaderKai)
- [springrain](https://github.com/springrain)
- [Shaozhou Hu](https://github.com/raspberry-hu)
- [finkyky](https://github.com/Finkyky)

Also, we receive many valuable issues, questions and advices from our community. Thanks all.

</detail>

+ 0
- 92
changes/1.1.0_zh.md View File

@@ -1,92 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.1.0

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.1.0

Seata-go 1.1.0 发布。

Seata-go 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

此版本更新如下:

### feature:

- [[#491](https://github.com/apache/incubator-seata-go/pull/491)] 支持查询全局事务锁
- [[#482](https://github.com/apache/incubator-seata-go/pull/482)] 支持 AT 模式 multi delete SQL 执行器
- [[#481](https://github.com/apache/incubator-seata-go/pull/481)] 支持 AT 模式 multi update SQL 执行器
- [[#478](https://github.com/apache/incubator-seata-go/pull/478)] 支持 AT 模式 select for update SQL 执行器
- [[#477](https://github.com/apache/incubator-seata-go/pull/477)] 支持 undo log 的 json 序列化方式
- [[#456](https://github.com/apache/incubator-seata-go/pull/456)] 支持 AT 模式 insert on update SQL 执行器
- [[#444](https://github.com/apache/incubator-seata-go/pull/444)] 支持 BZip 压缩算法
- [[#436](https://github.com/apache/incubator-seata-go/pull/436)] 支持读取 rm 相关的配置文件
- [[#433](https://github.com/apache/incubator-seata-go/pull/433)] 支持 xa 连接管理
- [[#430](https://github.com/apache/incubator-seata-go/pull/430)] 支持读取 getty 相关的配置文件

### bugfix:

- [[#509](https://github.com/apache/incubator-seata-go/pull/509)] 修复 AT 模式下执行 insert on update 时 undo log 的 SQLType 字段的问题
- [[#495](https://github.com/apache/incubator-seata-go/pull/495)] 修复 undo log 的 SQLType 字段的问题
- [[#487](https://github.com/apache/incubator-seata-go/pull/487)] 修复 AT 执行时出现的问题
- [[#472](https://github.com/apache/incubator-seata-go/pull/472)] 修复全局事务中上下文丢失值问题
- [[#461](https://github.com/apache/incubator-seata-go/pull/461)] 修复 error_code_test 中变量未定义导致的 ci 失败问题
- [[#459](https://github.com/apache/incubator-seata-go/pull/459)] 修复 error 日志重复打印问题
- [[#452](https://github.com/apache/incubator-seata-go/pull/452)] 修复 AT 模式 执行 insert SQL 时 id 自增的报错问题

### optimize:

- [[#507](https://github.com/apache/incubator-seata-go/pull/507)] 优化 AT 模式 multiple update SQL 执行器
- [[#505](https://github.com/apache/incubator-seata-go/pull/505)] 优化 AT 模式 multi SQL 执行器
- [[#453](https://github.com/apache/incubator-seata-go/pull/453)] 优化 messageType 和 transactionErrorCode 枚举值
- [[#447](https://github.com/apache/incubator-seata-go/pull/447)] 优化数据源初始化流程
- [[#466](https://github.com/apache/incubator-seata-go/pull/466)] 优化变量的命名

### test:

- [[#445](https://github.com/apache/incubator-seata-go/pull/445)] 添加 TransactionErrorCode 的单元测试

### doc:

- [[#492](https://github.com/apache/incubator-seata-go/pull/492)] 更新 readme 文件的已完成功能列表
- [[#489](https://github.com/apache/incubator-seata-go/pull/489)] 添加 1.1.0 版本的 change log

### contributors:

非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

- [luky116](https://github.com/luky116)
- [georgehao](https://github.com/georgehao)
- [lxfeng1997](https://github.com/lxfeng1997)
- [106umao](https://github.com/106umao)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/iSuperCoder)
- [Charlie17Li](https://github.com/Charlie17Li)
- [Code-Fight](https://github.com/Code-Fight)
- [Kirhaku](https://github.com/Kirhaku)
- [Vaderkai](https://github.com/VaderKai)
- [springrain](https://github.com/springrain)
- [Shaozhou Hu](https://github.com/raspberry-hu)
- [finkyky](https://github.com/Finkyky)

同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

</detail>


+ 0
- 80
changes/1.2.0.md View File

@@ -1,80 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.2.0

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.2.0

Seata-go 1.2.0 Released.

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:

- [[#534](https://github.com/apache/incubator-seata-go/pull/534)] support session load balance
- [[#535](https://github.com/apache/incubator-seata-go/pull/535)] add integrate test

### bugfix:

- [[#540](https://github.com/apache/incubator-seata-go/pull/540)] fix init xa panic bug
- [[#545](https://github.com/apache/incubator-seata-go/pull/545)] fix get db version bug
- [[#548](https://github.com/apache/incubator-seata-go/pull/548)] fix start xa failed bug
- [[#556](https://github.com/apache/incubator-seata-go/pull/556)] fix start xa driver failed bug
- [[#562](https://github.com/apache/incubator-seata-go/pull/562)] fix commit xa panic bug
- [[#564](https://github.com/apache/incubator-seata-go/pull/564)] fix commit xa branch bug
- [[#566](https://github.com/apache/incubator-seata-go/pull/566)] fix execute local tx bug

### optimize:

- [[#523](https://github.com/apache/incubator-seata-go/pull/523)] optimize the golang ci lint
- [[#525](https://github.com/apache/incubator-seata-go/pull/456)] rename parser name from jackson to json
- [[#532](https://github.com/apache/incubator-seata-go/pull/532)] remove duplicate code
- [[#536](https://github.com/apache/incubator-seata-go/pull/536)] format go import
- [[#554](https://github.com/apache/incubator-seata-go/pull/554)] optimize the performance of XA transactions
- [[#561](https://github.com/apache/incubator-seata-go/pull/561)] optimize xa output log

### test:


### doc:
- [[#550](https://github.com/apache/incubator-seata-go/pull/550)] add change-log of version 1.2.0


### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [georgehao](https://github.com/georgehao)
- [luky116](https://github.com/luky116)
- [jasondeng1997](https://github.com/jasondeng1997)
- [106umao](https://github.com/106umao)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/iSuperCoder)
- [Charlie17Li](https://github.com/Charlie17Li)
- [Code-Fight](https://github.com/Code-Fight)
- [Kirhaku](https://github.com/Kirhaku)
- [Vaderkai](https://github.com/VaderKai)


Also, we receive many valuable issues, questions and advices from our community. Thanks all.

</detail>

+ 0
- 83
changes/1.2.0_zh.md View File

@@ -1,83 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 1.2.0

<details>
<summary><mark>Release notes</mark></summary>

### Seata-go 1.2.0

Seata-go 1.2.0 发布。

Seata-go 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

此版本更新如下:

### feature:

- [[#534](https://github.com/apache/incubator-seata-go/pull/534)] 支持 session 的负载均衡
- [[#535](https://github.com/apache/incubator-seata-go/pull/535)] 添加加成测试

### bugfix:

- [[#540](https://github.com/apache/incubator-seata-go/pull/540)] 修复初始化 xa 模式的 bug
- [[#545](https://github.com/apache/incubator-seata-go/pull/545)] 修复 xa 模式获取 db 版本号的 bug
- [[#548](https://github.com/apache/incubator-seata-go/pull/548)] 修复启动 xa 时候会失败的 bug
- [[#556](https://github.com/apache/incubator-seata-go/pull/556)] 修复 xa 数据源的 bug
- [[#562](https://github.com/apache/incubator-seata-go/pull/562)] 修复提交 xa 全局事务的 bug
- [[#564](https://github.com/apache/incubator-seata-go/pull/564)] 修复提交 xa 分支事务的 bug
- [[#566](https://github.com/apache/incubator-seata-go/pull/566)] 修复使用 xa 数据源执行本地事务的 bug

### optimize:

- [[#523](https://github.com/apache/incubator-seata-go/pull/523)] 优化 CI 流程
- [[#525](https://github.com/apache/incubator-seata-go/pull/456)] 将 jackson 序列化重命名为 json
- [[#532](https://github.com/apache/incubator-seata-go/pull/532)] 移除重复的代码
- [[#536](https://github.com/apache/incubator-seata-go/pull/536)] 优化 go import 代码格式
- [[#554](https://github.com/apache/incubator-seata-go/pull/554)] 优化 xa 模式的性能
- [[#561](https://github.com/apache/incubator-seata-go/pull/561)] 优化 xa 模式的日志输出

### test:


### doc:
- [[#550](https://github.com/apache/incubator-seata-go/pull/550)] 添加 1.2.0 版本的改动日志


### contributors:

非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

- [georgehao](https://github.com/georgehao)
- [luky116](https://github.com/luky116)
- [jasondeng1997](https://github.com/jasondeng1997)
- [106umao](https://github.com/106umao)
- [wang1309](https://github.com/wang1309)
- [iSuperCoder](https://github.com/iSuperCoder)
- [Charlie17Li](https://github.com/Charlie17Li)
- [Code-Fight](https://github.com/Code-Fight)
- [Kirhaku](https://github.com/Kirhaku)
- [Vaderkai](https://github.com/VaderKai)




同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

</detail>


+ 0
- 173
changes/2.0.0.md View File

@@ -1,173 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 2.0.0

<details>
<summary><mark>Release notes</mark></summary>
### Seata-go 2.0.0

Seata-go 2.0.0 Released.

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:

- [[#761](https://github.com/apache/incubator-seata-go/pull/761)]Support Update join
- [[#806](https://github.com/apache/incubator-seata-go/pull/806)]Add Release Drafter configuration files
- [[#659](https://github.com/apache/incubator-seata-go/pull/659)] support compress for AT undo log
- [[#574](https://github.com/apache/incubator-seata-go/pull/574)] support file and nacos service registry
- [[#584](https://github.com/apache/incubator-seata-go/pull/584)] support the ConsistentHash load balancing strategy in the remoting module
- [[#585](https://github.com/apache/incubator-seata-go/pull/585)] support the LeastActive load balancing strategy in the remoting module
- [[#605](https://github.com/apache/incubator-seata-go/pull/605)] support the discovery service of Etcd
- [[#622](https://github.com/apache/incubator-seata-go/pull/622)] add round robin strategy of remote call
- [[#691](https://github.com/apache/incubator-seata-go/pull/691)] support protobuf undo log parser
- [[#738](https://github.com/apache/incubator-seata-go/pull/738)] remove session when send heart beat message failed
- [[#739](https://github.com/apache/incubator-seata-go/pull/739)] support automatic refresh functionality for table meta cache

### bugfix:

- [[#877](https://github.com/apache/incubator-seata-go/pull/857)]del metadatacache uppertablenamekey and increase tablemeta field uppertablename

- [[#861](https://github.com/apache/incubator-seata-go/pull/861)]update project icon in readme
- [[#834](https://github.com/apache/incubator-seata-go/pull/834)]Solve the conflict problem of introducing multiple versions of knadh
- [[#839](https://github.com/apache/incubator-seata-go/pull/839)]fix action errors
- [[#850](https://github.com/apache/incubator-seata-go/pull/850)]fix failed parsing table of delete sql
- [[#823](https://github.com/apache/incubator-seata-go/pull/823)]Remove issue translation workflow
- [[#820](https://github.com/apache/incubator-seata-go/pull/820)]Fix possible vulnerabilities caused by common libraries
- [[#810](https://github.com/apache/incubator-seata-go/pull/810)]fix transaction failed ,when using queryContext
- [[#813](https://github.com/apache/incubator-seata-go/pull/813)]add some licenser header
- [[#771](https://github.com/apache/incubator-seata-go/pull/771)]mysql insert on update duplicate sensitive case not matched
- [[#797](https://github.com/apache/incubator-seata-go/pull/797)] add ASF header to some files
- [[#781](https://github.com/apache/incubator-seata-go/pull/781)] Fixed that the same record has different lowkeys due to mixed case of table names
- [[#780](https://github.com/apache/incubator-seata-go/pull/780)] failed to decode ColumnImage mysql:text type by json
- [[#782](https://github.com/apache/incubator-seata-go/pull/782)] failed to decode ColumnImage mysql:text type by json
- [[#789](https://github.com/apache/incubator-seata-go/pull/789)] add 2025 to NOTICE
- [[#776](https://github.com/apache/incubator-seata-go/pull/776)] fix ci-lint typecheck error
- [[#540](https://github.com/apache/incubator-seata-go/pull/540)] fix init XA panic bug
- [[#590](https://github.com/apache/incubator-seata-go/pull/590)] fix some repo error
- [[#595](https://github.com/apache/incubator-seata-go/pull/595)] check the response error is nil for commit or rollback
- [[#607](https://github.com/apache/incubator-seata-go/pull/607)] fix the bug of jackson serialize
- [[#665](https://github.com/apache/incubator-seata-go/pull/665)] reclaim the heartbeat response message to avoid memory leakage of GettyRemoting.future
- [[#672](https://github.com/apache/incubator-seata-go/pull/672)] fix AT rollback bug
- [[#674](https://github.com/apache/incubator-seata-go/pull/674)] fix XA rollback bug
- [[#690](https://github.com/apache/incubator-seata-go/pull/690)] fix AT undo log jackson parser not found bug
- [[#701](https://github.com/apache/incubator-seata-go/pull/701)] fix the InsertOnDuplicateUpdate is an issue with bypassing modifying the primary key
- [[#717](https://github.com/apache/incubator-seata-go/pull/717)] support XA report state to TC
- [[#724](https://github.com/apache/incubator-seata-go/pull/724)] support ParenthesesExpr for SQL parser
- [[#736](https://github.com/apache/incubator-seata-go/pull/736)] fix SQL statement not closed's bug
- [[#743](https://github.com/apache/incubator-seata-go/pull/743)] fix bug of gomonkey
- [[#749](https://github.com/apache/incubator-seata-go/pull/749)] fix bug of heart beat


### optimize:

- [[#837](https://github.com/apache/incubator-seata-go/pull/837)]AT model optimize build lock key performance
- [[#824](https://github.com/apache/incubator-seata-go/pull/824)]update SHA256 checksum command in makefile for cross-platform compatibility
- [[#777](https://github.com/apache/incubator-seata-go/pull/777)]optimize transaction timeout judgment
- [[#786](https://github.com/apache/incubator-seata-go/pull/786)]support ipv6
- [[#802](https://github.com/apache/incubator-seata-go/pull/802)]support get db version in conn
- [[#745](https://github.com/apache/incubator-seata-go/pull/745)]optimized the daily deletion of fence logs
- [[#767](https://github.com/apache/incubator-seata-go/pull/767)]upgrade some dependent packages to eliminate dependencies on some archived repositories
- [[#768](https://github.com/apache/incubator-seata-go/pull/768)]update parser to v0.2.17
- [[#576](https://github.com/apache/incubator-seata-go/pull/576)] use mirromutth/mysql-action instead of icomponent/mysql-action
- [[#594](https://github.com/apache/incubator-seata-go/pull/594)] optimize the log of branch commit procesor
- [[#621](https://github.com/apache/incubator-seata-go/pull/621)] add codeql for ci
- [[#631](https://github.com/apache/incubator-seata-go/pull/631)] upgrade crypto version from 0.9.0 to 0.17.0
- [[#652](https://github.com/apache/incubator-seata-go/pull/652)] upgrade gRPC version from 1.51.0 ro 1.56.3
- [[#667](https://github.com/apache/incubator-seata-go/pull/667)] change mailbox of issues and pull requests from dev to notifications
- [[#678](https://github.com/apache/incubator-seata-go/pull/678)] rename module name to seata.apache.org/seata-go
- [[#679](https://github.com/apache/incubator-seata-go/pull/679)] upgrade getty version from 1.4.9 to 1.4.10
- [[#714](https://github.com/apache/incubator-seata-go/pull/714)] optimize the speed of build lock key
- [[#719](https://github.com/apache/incubator-seata-go/pull/719)] only save insertd filed when execute insert SQL in AT
- [[#721](https://github.com/apache/incubator-seata-go/pull/721)] fix the issue where the translation bot is not working
- [[#758](https://github.com/apache/incubator-seata-go/pull/758)] remove unusen files

### test:

- [[#570](https://github.com/apache/incubator-seata-go/pull/570)] add collection unit test
- [[#571](https://github.com/apache/incubator-seata-go/pull/571)] add convert unit test
- [[#572](https://github.com/apache/incubator-seata-go/pull/572)] add reflectx unit test
- [[#5835f0](https://github.com/apache/incubator-seata-go/commit/5835f09ecfd6edeb04c2961163bc4460f578e942)] add random loadbalance unit test
- [[#599](https://github.com/apache/incubator-seata-go/pull/599)] add xid loadbalance unit test


### doc:

- [[#844](https://github.com/apache/incubator-seata-go/pull/844)]Enrich the project ReadMe
- [[#760](https://github.com/apache/incubator-seata-go/pull/760)]V2.0.0 release updater
- [[#614](https://github.com/apache/incubator-seata-go/pull/614)] upgrade the unknown license dependency
- [[#632](https://github.com/apache/incubator-seata-go/pull/632)] add ASF basic config
- [[#633](https://github.com/apache/incubator-seata-go/pull/633)] optimize ASF basic config to remove th context check
- [[#644](https://github.com/apache/incubator-seata-go/pull/644)] optimize readme file
- [[#686](https://github.com/apache/incubator-seata-go/pull/686)] add more linter in ci
- [[#737](https://github.com/apache/incubator-seata-go/pull/737)] modify the readme file and update the currently completed work
- [[#756](https://github.com/apache/incubator-seata-go/pull/756)] update license checker


### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [luky116](https://github.com/luky116)
- [Code-Fight](https://github.com/Code-Fight)
- [wt-better](https://github.com/wt-better)
- [luweiqianyi](https://github.com/luweiqianyi)
- [wang1309](https://github.com/wang1309)
- [576470954](https://github.com/576470954)
- [No-SilverBullet](https://github.com/No-SilverBullet)
- [solisamicus](https://github.com/solisamicus)
- [marsevilspirit](https://github.com/marsevilspirit)
- [lxfeng1997](https://github.com/lxfeng1997)
- [AlexStocks](https://github.com/AlexStocks)
- [smiletrl](https://github.com/smiletrl)
- [ptyin](https://github.com/ptyin)
- [yizhibian](https://github.com/yizhibian)
- [oldmee](https://github.com/oldmee)
- [air-3](https://github.com/air-3)
- [slievrly](https://github.com/slievrly)
- [xjlgod](https://github.com/xjlgod)
- [baerwang](https://github.com/baerwang)
- [xyombo](https://github.com/xyombo)
- [testwill](https://github.com/testwill)
- [jasondeng1997](https://github.com/jasondeng1997)
- [jsbxyyx](https://github.com/jsbxyyx)
- [iSuperCoder](https://github.com/iSuperCoder)
- [georgehao](https://github.com/georgehao)
- [liuyuecai](https://github.com/liuyuecai)
- [106umao](https://github.com/106umao)
- [FinnTew](https://github.com/FinnTew)
- [funky-eyes](https://github.com/funky-eyes)
- [tanzegen](https://github.com/tanzegen)
- [lovepoem](https://github.com/lovepoem)
- [MinatoWu](https://github.com/MinatoWu)
- [LucienShen-Liu](https://github.com/LucienShen-Liu)
- [panlei-coder](https://github.com/panlei-coder)
- [lixingjia77](https://github.com/lixingjia77)
- [Road2Melon](https://github.com/Road2Melon)
- [Similarityoung](https://github.com/Similarityoung)
- [YvCeung](https://github.com/YvCeung)
- [pjfanning](https://github.com/pjfanning)
- [hokkine](https://github.com/hokkine)
- [zhangymPerson](https://github.com/zhangymPerson)
- [ForestLH](https://github.com/ForestLH)

Also, we receive many valuable issues, questions and advices from our community. Thanks all.

</detail>

+ 0
- 169
changes/2.0.0_zh.md View File

@@ -1,169 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 2.0.0

<details>
<summary><mark>版本变更</mark></summary>
# Seata-go 2.0.0

Seata-go 2.0.0 已发布。

Seata-go 是一个易于使用、高性能的开源分布式事务解决方案。

本版本更新内容如下:

## 新增特性(feature):

- [[#761](https://github.com/apache/incubator-seata-go/pull/761)] 支持 Update join。
- [[#806](https://github.com/apache/incubator-seata-go/pull/806)] 新增 Release Drafter 配置文件。
- [[#659](https://github.com/apache/incubator-seata-go/pull/659)] 为 AT undo log 支持压缩。
- [[#574](https://github.com/apache/incubator-seata-go/pull/574)] 支持基于文件(file)和 Nacos 的服务注册。
- [[#584](https://github.com/apache/incubator-seata-go/pull/584)] 在 remoting 模块中支持 ConsistentHash 负载均衡策略。
- [[#585](https://github.com/apache/incubator-seata-go/pull/585)] 在 remoting 模块中支持 LeastActive 负载均衡策略。
- [[#605](https://github.com/apache/incubator-seata-go/pull/605)] 支持 Etcd 的服务发现。
- [[#622](https://github.com/apache/incubator-seata-go/pull/622)] 为远程调用新增轮询(round robin)策略。
- [[#691](https://github.com/apache/incubator-seata-go/pull/691)] 支持 protobuf 格式的 undo log 解析器。
- [[#738](https://github.com/apache/incubator-seata-go/pull/738)] 在发送心跳消息失败时移除会话。
- [[#739](https://github.com/apache/incubator-seata-go/pull/739)] 支持表元数据缓存的自动刷新功能。

## 修复(bugfix):

- [[#877](https://github.com/apache/incubator-seata-go/pull/857)] 删除 metadatacache 的 uppertablenamekey,并在 tablemeta 中增加 uppertablename 字段。
- [[#861](https://github.com/apache/incubator-seata-go/pull/861)] 更新 README 中的项目图标。
- [[#834](https://github.com/apache/incubator-seata-go/pull/834)] 解决引入多个版本 knadh 导致的冲突问题。
- [[#839](https://github.com/apache/incubator-seata-go/pull/839)] 修复 Action 错误。
- [[#850](https://github.com/apache/incubator-seata-go/pull/850)] 修复删除类 SQL 的表解析失败问题。
- [[#823](https://github.com/apache/incubator-seata-go/pull/823)] 移除 issue 翻译工作流。
- [[#820](https://github.com/apache/incubator-seata-go/pull/820)] 修复由通用库引起的潜在安全漏洞。
- [[#810](https://github.com/apache/incubator-seata-go/pull/810)] 修复在使用 queryContext 时导致事务失败的问题。
- [[#813](https://github.com/apache/incubator-seata-go/pull/813)] 为若干文件添加许可头。
- [[#771](https://github.com/apache/incubator-seata-go/pull/771)] 修复 MySQL `INSERT ... ON DUPLICATE UPDATE` 在大小写敏感场景下匹配不正确的问题。
- [[#797](https://github.com/apache/incubator-seata-go/pull/797)] 为部分文件添加 ASF 头。
- [[#781](https://github.com/apache/incubator-seata-go/pull/781)] 修复因表名大小写混合导致相同记录出现不同 lowkeys 的问题。
- [[#780](https://github.com/apache/incubator-seata-go/pull/780)] 修复无法通过 JSON 解码 MySQL `TEXT` 类型的 ColumnImage 的问题。
- [[#782](https://github.com/apache/incubator-seata-go/pull/782)] 修复无法通过 JSON 解码 MySQL `TEXT` 类型的 ColumnImage 的问题(重复修复项)。
- [[#789](https://github.com/apache/incubator-seata-go/pull/789)] 在 NOTICE 中添加 2025 年。
- [[#776](https://github.com/apache/incubator-seata-go/pull/776)] 修复 CI lint 类型检查错误。
- [[#540](https://github.com/apache/incubator-seata-go/pull/540)] 修复初始化 XA 时的 panic 错误。
- [[#590](https://github.com/apache/incubator-seata-go/pull/590)] 修复若干仓库错误。
- [[#595](https://github.com/apache/incubator-seata-go/pull/595)] 在提交(commit)或回滚(rollback)时检查响应错误是否为 nil。
- [[#607](https://github.com/apache/incubator-seata-go/pull/607)] 修复 Jackson 序列化的相关 bug。
- [[#665](https://github.com/apache/incubator-seata-go/pull/665)] 回收心跳响应消息,以避免 GettyRemoting.future 的内存泄漏。
- [[#672](https://github.com/apache/incubator-seata-go/pull/672)] 修复 AT 回滚的错误。
- [[#674](https://github.com/apache/incubator-seata-go/pull/674)] 修复 XA 回滚的错误。
- [[#690](https://github.com/apache/incubator-seata-go/pull/690)] 修复 AT undo log 的 Jackson 解析器未找到的问题。
- [[#701](https://github.com/apache/incubator-seata-go/pull/701)] 修复 InsertOnDuplicateUpdate 绕过主键修改所导致的问题。
- [[#717](https://github.com/apache/incubator-seata-go/pull/717)] 支持 XA 向 TC 报告状态。
- [[#724](https://github.com/apache/incubator-seata-go/pull/724)] 为 SQL 解析器支持 ParenthesesExpr(括号表达式)。
- [[#736](https://github.com/apache/incubator-seata-go/pull/736)] 修复 SQL 语句未正确关闭的问题。
- [[#743](https://github.com/apache/incubator-seata-go/pull/743)] 修复 gomonkey 相关的 bug。
- [[#749](https://github.com/apache/incubator-seata-go/pull/749)] 修复心跳相关的 bug。

## 优化(optimize):

- [[#837](https://github.com/apache/incubator-seata-go/pull/837)] 优化 AT 模型中构建锁键的性能。
- [[#824](https://github.com/apache/incubator-seata-go/pull/824)] 更新 Makefile 中的 SHA256 校验命令以兼容跨平台。
- [[#777](https://github.com/apache/incubator-seata-go/pull/777)] 优化事务超时判断逻辑。
- [[#786](https://github.com/apache/incubator-seata-go/pull/786)] 支持 IPv6。
- [[#802](https://github.com/apache/incubator-seata-go/pull/802)] 在连接中支持获取数据库版本。
- [[#745](https://github.com/apache/incubator-seata-go/pull/745)] 优化 fence 日志的每日删除策略。
- [[#767](https://github.com/apache/incubator-seata-go/pull/767)] 升级若干依赖包以消除对部分已归档仓库的依赖。
- [[#768](https://github.com/apache/incubator-seata-go/pull/768)] 将解析器更新至 v0.2.17。
- [[#576](https://github.com/apache/incubator-seata-go/pull/576)] 在 CI 中使用 mirromutth/mysql-action 替换 icomponent/mysql-action。
- [[#594](https://github.com/apache/incubator-seata-go/pull/594)] 优化分支提交处理器的日志。
- [[#621](https://github.com/apache/incubator-seata-go/pull/621)] 为 CI 添加 CodeQL。
- [[#631](https://github.com/apache/incubator-seata-go/pull/631)] 将 crypto 版本从 0.9.0 升级至 0.17.0。
- [[#652](https://github.com/apache/incubator-seata-go/pull/652)] 将 gRPC 版本从 1.51.0 升级至 1.56.3。
- [[#667](https://github.com/apache/incubator-seata-go/pull/667)] 将 issue 与 pull request 的邮箱由 dev 更改为 notifications。
- [[#678](https://github.com/apache/incubator-seata-go/pull/678)] 将模块名重命名为 `seata.apache.org/seata-go`。
- [[#679](https://github.com/apache/incubator-seata-go/pull/679)] 将 getty 版本从 1.4.9 升级至 1.4.10。
- [[#714](https://github.com/apache/incubator-seata-go/pull/714)] 优化构建锁键的速度。
- [[#719](https://github.com/apache/incubator-seata-go/pull/719)] 在 AT 执行 INSERT SQL 时仅保存被插入的字段。
- [[#721](https://github.com/apache/incubator-seata-go/pull/721)] 修复翻译机器人不可用的问题。
- [[#758](https://github.com/apache/incubator-seata-go/pull/758)] 移除未使用的文件。

## 测试(test):

- [[#570](https://github.com/apache/incubator-seata-go/pull/570)] 添加 collection 单元测试。
- [[#571](https://github.com/apache/incubator-seata-go/pull/571)] 添加 convert 单元测试。
- [[#572](https://github.com/apache/incubator-seata-go/pull/572)] 添加 reflectx 单元测试。
- [[#5835f0](https://github.com/apache/incubator-seata-go/commit/5835f09ecfd6edeb04c2961163bc4460f578e942)] 添加 random loadbalance 单元测试。
- [[#599](https://github.com/apache/incubator-seata-go/pull/599)] 添加 xid loadbalance 单元测试。

## 文档(doc):

- [[#844](https://github.com/apache/incubator-seata-go/pull/844)] 丰富项目 README。
- [[#760](https://github.com/apache/incubator-seata-go/pull/760)] V2.0.0 发布更新器。
- [[#614](https://github.com/apache/incubator-seata-go/pull/614)] 升级具有未知许可证的依赖。
- [[#632](https://github.com/apache/incubator-seata-go/pull/632)] 添加 ASF 基本配置。
- [[#633](https://github.com/apache/incubator-seata-go/pull/633)] 优化 ASF 基本配置以移除上下文检查。
- [[#644](https://github.com/apache/incubator-seata-go/pull/644)] 优化 README 文件。
- [[#686](https://github.com/apache/incubator-seata-go/pull/686)] 在 CI 中添加更多 linter。
- [[#737](https://github.com/apache/incubator-seata-go/pull/737)] 修改 README 并更新当前已完成的工作项。
- [[#756](https://github.com/apache/incubator-seata-go/pull/756)] 更新许可检查器。

## 贡献者(contributors):

感谢以下贡献者的代码提交。若有遗漏请告知。

- [luky116](https://github.com/luky116)
- [Code-Fight](https://github.com/Code-Fight)
- [wt-better](https://github.com/wt-better)
- [luweiqianyi](https://github.com/luweiqianyi)
- [wang1309](https://github.com/wang1309)
- [576470954](https://github.com/576470954)
- [No-SilverBullet](https://github.com/No-SilverBullet)
- [solisamicus](https://github.com/solisamicus)
- [marsevilspirit](https://github.com/marsevilspirit)
- [lxfeng1997](https://github.com/lxfeng1997)
- [AlexStocks](https://github.com/AlexStocks)
- [smiletrl](https://github.com/smiletrl)
- [ptyin](https://github.com/ptyin)
- [yizhibian](https://github.com/yizhibian)
- [oldmee](https://github.com/oldmee)
- [air-3](https://github.com/air-3)
- [slievrly](https://github.com/slievrly)
- [xjlgod](https://github.com/xjlgod)
- [baerwang](https://github.com/baerwang)
- [xyombo](https://github.com/xyombo)
- [testwill](https://github.com/testwill)
- [jasondeng1997](https://github.com/jasondeng1997)
- [jsbxyyx](https://github.com/jsbxyyx)
- [iSuperCoder](https://github.com/iSuperCoder)
- [georgehao](https://github.com/georgehao)
- [liuyuecai](https://github.com/liuyuecai)
- [106umao](https://github.com/106umao)
- [FinnTew](https://github.com/FinnTew)
- [funky-eyes](https://github.com/funky-eyes)
- [tanzegen](https://github.com/tanzegen)
- [lovepoem](https://github.com/lovepoem)
- [MinatoWu](https://github.com/MinatoWu)
- [LucienShen-Liu](https://github.com/LucienShen-Liu)
- [panlei-coder](https://github.com/panlei-coder)
- [lixingjia77](https://github.com/lixingjia77)
- [Road2Melon](https://github.com/Road2Melon)
- [Similarityoung](https://github.com/Similarityoung)
- [YvCeung](https://github.com/YvCeung)
- [pjfanning](https://github.com/pjfanning)
- [hokkine](https://github.com/hokkine)
- [zhangymPerson](https://github.com/zhangymPerson)
- [ForestLH](https://github.com/ForestLH)

此外,我们从社区收到了许多有价值的问题、提问与建议,特此致谢。

</detail>

+ 0
- 48
changes/dev.md View File

@@ -1,48 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### Development notes

### dev version

Seata-go is an easy-to-use, high-performance, open source distributed transaction solution.

The version is updated as follows:

### feature:

- [[#123](https://github.com/apache/incubator-seata-go/pull/123)] add two phase and dubbo

### bugfix:

- [[#130](https://github.com/apache/incubator-seata-go/pull/130)] getty session auto close bug

### optimize:

- [[#125](https://github.com/apache/incubator-seata-go/pull/125)] optimize named for the resource manager api and tcc resource

### test:

- [[#xxx](https://github.com/apache/incubator-seata-go/pull/xxx)] test case for xxx

### contributors:

Thanks to these contributors for their code commits. Please report an unintended omission.

- [slievrly](https://github.com/slievrly)

Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.

+ 0
- 50
changes/dev_zh.md View File

@@ -1,50 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### 开发记录


### dev version

Seata-go 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

此版本更新如下:

### feature:

- [[#123](https://github.com/apache/incubator-seata-go/pull/123)] 添加二阶段事务接口,以及dubbo集成

### bugfix:

- [[#130](https://github.com/apache/incubator-seata-go/pull/130)] 修复getty session自动关闭的bug

### optimize:

- [[#125](https://github.com/apache/incubator-seata-go/pull/125)] 优化resourceManagerApi和tccResource功能

### test:

- [[#xxx](https://github.com/apache/incubator-seata-go/pull/xxx)] 添加xxx的单元测试


### contributors:

非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

- [slievrly](https://github.com/slievrly)

同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

+ 0
- 22
cmd/start.go View File

@@ -1,22 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package main

func main() {
// start the server
}

+ 26
- 0
common/xid.go View File

@@ -0,0 +1,26 @@
package common

import (
"fmt"
"strconv"
"strings"
)

type XId struct {
Port int
IpAddress string
}

var XID = &XId{}

func (xId *XId) GenerateXID(tranId int64) string {
return fmt.Sprintf("%s:%d:%d",xId.IpAddress,xId.Port,tranId)
}

func (xId *XId) GetTransactionId(xid string) int64 {
if xid == "" { return -1 }

idx := strings.LastIndex(xid,":")
tranId,_ := strconv.ParseInt(xid[idx:],10,64)
return tranId
}

+ 1
- 0
docs/seata-golang.svg
File diff suppressed because it is too large
View File


+ 10
- 108
go.mod View File

@@ -1,115 +1,17 @@
module seata.apache.org/seata-go
module github.com/dk-lockdown/seata-golang

go 1.20
go 1.13.3

require (
dubbo.apache.org/dubbo-go/v3 v3.0.4
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/apache/dubbo-getty v1.5.0
github.com/arana-db/parser v0.2.17
github.com/bluele/gcache v0.0.2
github.com/dsnet/compress v0.0.1
github.com/dubbogo/gost v1.13.2
github.com/gin-gonic/gin v1.9.1
github.com/go-sql-driver/mysql v1.6.0
github.com/goccy/go-json v0.10.2
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/pierrec/lz4/v4 v4.1.17
github.com/dubbogo/getty v1.3.3
github.com/dubbogo/gost v1.6.0
github.com/gorilla/websocket v1.4.1 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.12.2
github.com/prometheus/common v0.32.1
github.com/sijms/go-ora/v2 v2.5.17
github.com/stretchr/testify v1.8.3
go.uber.org/atomic v1.9.0
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.56.3
gopkg.in/yaml.v2 v2.4.0
github.com/stretchr/testify v1.5.1
go.uber.org/atomic v1.5.0
go.uber.org/zap v1.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.8
vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10
)

require (
github.com/agiledragon/gomonkey/v2 v2.12.0
github.com/golang/protobuf v1.5.3
go.etcd.io/etcd/api/v3 v3.5.6
go.etcd.io/etcd/client/v3 v3.5.6
google.golang.org/protobuf v1.30.0
)

require github.com/knadh/koanf v1.5.0

require (
github.com/RoaringBitmap/roaring v1.2.0 // indirect
github.com/Workiva/go-datastructures v1.0.52 // indirect
github.com/apache/dubbo-go-hessian2 v1.11.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/creasty/defaults v1.5.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jinzhu/copier v0.3.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/pp v3.0.1+incompatible // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect
github.com/shirou/gopsutil/v3 v3.22.2 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.6 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require (
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/klauspost/compress v1.15.11
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/pingcap/log v0.0.0-20210906054005-afc726e70354 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
vimagination.zapto.org/memio v0.0.0-20200222190306-588ebc67b97d // indirect
)

replace github.com/dubbogo/gost => github.com/dubbogo/gost v1.13.2

exclude github.com/polarismesh/polaris-go v1.3.0-alpha

+ 31
- 1242
go.sum
File diff suppressed because it is too large
View File


+ 0
- 29
goimports.sh View File

@@ -1,29 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# format go imports style
go install golang.org/x/tools/cmd/goimports
goimports -local seata.apache.org/seata-go -w .

# format licence style
go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
license-eye header fix
# check dependency licence is valid
license-eye dependency check

# format go.mod
go mod tidy

+ 0
- 48
integrate_test.sh View File

@@ -1,48 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#!/bin/bash

set -e
set -x

echo 'start integrate-test'

# set root workspace
ROOT_DIR=$(pwd)
echo "integrate-test root work-space -> ${ROOT_DIR}"

# show all github-env
echo "github current commit id -> $2"
echo "github pull request branch -> ${GITHUB_REF}"
echo "github pull request slug -> ${GITHUB_REPOSITORY}"
echo "github pull request repo slug -> ${GITHUB_REPOSITORY}"
echo "github pull request actor -> ${GITHUB_ACTOR}"
echo "github pull request repo param -> $1"
echo "github pull request base branch -> $3"
echo "github pull request head branch -> ${GITHUB_HEAD_REF}"

echo "use seata-go-samples $3 branch for integration testing"
git clone https://github.com/apache/incubator-seata-go-samples samples && cd samples

# update seata-go to current commit id

go mod edit -replace=seata.apache.org/seata-go=github.com/"$1"@"$2"

go mod tidy

# start integrate test
./start_integrate_test.sh

+ 0
- 9
licenses/LICENSE.tpl View File

@@ -1,9 +0,0 @@
{{.LicenseContent }}
{{ range .Groups }}
========================================================================
{{.LicenseID}} licenses
========================================================================
{{range .Deps}}
{{.Name}} {{.Version}} {{.LicenseID}}
{{- end }}
{{ end }}

+ 186
- 0
logging/logging.go View File

@@ -0,0 +1,186 @@
package logging

import (
"fmt"
"log"
"os"
)

// Level represents the level of logging.
type LogLevel uint8

const (
Debug LogLevel = iota
Info
Warn
Error
Fatal
Panic
)

type ILogger interface {
Debug(v ...interface{})
Debugf(format string, v ...interface{})

Info(v ...interface{})
Infof(format string, v ...interface{})

Warn(v ...interface{})
Warnf(format string, v ...interface{})

Error(v ...interface{})
Errorf(format string, v ...interface{})

Fatal(v ...interface{})
Fatalf(format string, v ...interface{})

Panic(v ...interface{})
Panicf(format string, v ...interface{})
}

const (
DefaultLogLevel = Info
DefaultNamespace = "default"
)

type SeataLogger struct {
loggers []*log.Logger
namespace string
logLevel LogLevel
}

var Logger *SeataLogger

func init() {
var loggers = make([]*log.Logger, 0)
loggers = append(loggers, log.New(os.Stdout, "", log.LstdFlags))
Logger = &SeataLogger{
loggers: loggers,
namespace: DefaultNamespace,
logLevel: DefaultLogLevel,
}
}

func merge(namespace, logLevel, msg string) string {
return fmt.Sprintf("%s %s %s", namespace, logLevel, msg)
}

func SetNamespace(namespace string) {
Logger.namespace = namespace
}

func SetLogLevel(logLevel LogLevel) {
Logger.logLevel = logLevel
}

func AddLogger(logger *log.Logger) {
Logger.loggers = append(Logger.loggers, logger)
}

func (l *SeataLogger) Debug(v ...interface{}) {
if Debug < l.logLevel || len(v) == 0 {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "DEBUG", fmt.Sprint(v...)))
}
}

func (l *SeataLogger) Debugf(format string, v ...interface{}) {
if Debug < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "DEBUG", fmt.Sprintf(format, v...)))
}
}

func (l *SeataLogger) Info(v ...interface{}) {
if Info < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "INFO", fmt.Sprint(v...)))
}
}

func (l *SeataLogger) Infof(format string, v ...interface{}) {
if Info < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "INFO", fmt.Sprintf(format, v...)))
}
}

func (l *SeataLogger) Warn(v ...interface{}) {
if Warn < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "WARNING", fmt.Sprint(v...)))
}
}

func (l *SeataLogger) Warnf(format string, v ...interface{}) {
if Warn < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "WARNING", fmt.Sprintf(format, v...)))
}
}

func (l *SeataLogger) Error(v ...interface{}) {
if Error < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "ERROR", fmt.Sprint(v...)))
}
}

func (l *SeataLogger) Errorf(format string, v ...interface{}) {
if Error < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "ERROR", fmt.Sprintf(format, v...)))
}
}

func (l *SeataLogger) Fatal(v ...interface{}) {
if Fatal < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "FATAL", fmt.Sprint(v...)))
}
}

func (l *SeataLogger) Fatalf(format string, v ...interface{}) {
if Fatal < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "FATAL", fmt.Sprintf(format, v...)))
}
}

func (l *SeataLogger) Panic(v ...interface{}) {
if Panic < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "PANIC", fmt.Sprint(v...)))
}
}

func (l *SeataLogger) Panicf(format string, v ...interface{}) {
if Panic < l.logLevel {
return
}
for _,log := range l.loggers {
log.Print(merge(l.namespace, "PANIC", fmt.Sprintf(format, v...)))
}
}

+ 7
- 0
logging/logging_test.go View File

@@ -0,0 +1,7 @@
package logging

import "testing"

func TestSeataLogger_Info(t *testing.T) {
Logger.Info("there is a bug")
}

+ 0
- 80
makefile View File

@@ -1,80 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

VERSION=$(shell cat "./VERSION" 2> /dev/null)

GO_FLAGS := -ldflags "-X main.Branch=$(GIT_BRANCH) -X main.Revision=$(GIT_REVISION) -X main.Version=$(VERSION) -extldflags \"-static\" -s -w" -tags netgo
GO = go
GO_PATH = $(shell $(GO) env GOPATH)
GO_OS = $(shell $(GO) env GOOS)
ifeq ($(GO_OS), darwin)
GO_OS = mac
endif
ifeq ($(shell uname -s), Darwin)
SHA256_CMD = shasum -a 256
else
SHA256_CMD = sha256sum
endif

# License environment
GO_LICENSE_CHECKER_DIR = license-header-checker-$(GO_OS)
GO_LICENSE_CHECKER = $(GO_PATH)/bin/license-header-checker
LICENSE_DIR = /tmp/tools/license

# format import code
format-import:
go get -d github.com/dubbogo/tools/cmd/imports-formatter
imports-formatter -path . -module seata.apache.org/seata-go -bl false

unit-test:
go test ./pkg/... -coverprofile=coverage.txt -covermode=atomic

# Generate binaries for a Cortex release
dist dist/seatago-linux-amd64 dist/seatago-darwin-amd64 dist/seatago-linux-amd64-sha-256 dist/seatago-darwin-amd64-sha-256:
rm -fr ./dist
mkdir -p ./dist
GOOS="linux" GOARCH="amd64" CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago-linux-amd64 ./cmd
GOOS="darwin" GOARCH="amd64" CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago-darwin-amd64 ./cmd
$(SHA256_CMD) ./dist/seatago-darwin-amd64 | cut -d ' ' -f 1 > ./dist/seatago-darwin-amd64-sha-256
$(SHA256_CMD) ./dist/seatago-linux-amd64 | cut -d ' ' -f 1 > ./dist/seatago-linux-amd64-sha-256

# Generate binaries for a Cortex release
build dist/seatago dist/seatago-sha-256:
rm -fr ./dist
mkdir -p ./dist
CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago ./cmd
$(SHA256_CMD) ./dist/seatago | cut -d ' ' -f 1 > ./dist/seatago-sha-256

#docker-build:
# docker build -t seatago/seatago:latest .

integration-test:
@go clean -testcache
go test -tags integration -v ./test/...

clean:
@rm -rf coverage.txt
@rm -rf dist

prepareLic:
echo 'The makefile is for ci test and has dependencies. Do not run it locally. If you want to run the unit tests, run command `go test ./...` directly.'
$(GO_LICENSE_CHECKER) -version || (wget https://github.com/lsm-dev/license-header-checker/releases/download/v1.2.0/$(GO_LICENSE_CHECKER_DIR).zip -O $(GO_LICENSE_CHECKER_DIR).zip && unzip -o $(GO_LICENSE_CHECKER_DIR).zip && mkdir -p $(GO_PATH)/bin/ && cp $(GO_LICENSE_CHECKER_DIR)/64bit/license-header-checker $(GO_PATH)/bin/)
ls /tmp/tools/license/license.txt || wget -P $(LICENSE_DIR) https://github.com/dubbogo/resources/raw/master/tools/license/license.txt

.PHONY: license
license: prepareLic
$(GO_LICENSE_CHECKER) -v -a -r -i vendor $(LICENSE_DIR)/license.txt . go && [[ -z `git status -s` ]]

+ 102
- 0
meta/branch_status.go View File

@@ -0,0 +1,102 @@
package meta

import "fmt"

type BranchStatus byte

const (
/**
* The BranchStatus_Unknown.
* description:BranchStatus_Unknown branch status.
*/
BranchStatusUnknown BranchStatus = iota

/**
* The BranchStatus_Registered.
* description:BranchStatus_Registered to TC.
*/
BranchStatusRegistered

/**
* The Phase one done.
* description:Branch logic is successfully done at phase one.
*/
BranchStatusPhaseoneDone

/**
* The Phase one failed.
* description:Branch logic is failed at phase one.
*/
BranchStatusPhaseoneFailed

/**
* The Phase one timeout.
* description:Branch logic is NOT reported for a timeout.
*/
BranchStatusPhaseoneTimeout

/**
* The Phase two committed.
* description:Commit logic is successfully done at phase two.
*/
BranchStatusPhasetwoCommitted

/**
* The Phase two commit failed retryable.
* description:Commit logic is failed but retryable.
*/
BranchStatusPhasetwoCommitFailedRetryable

/**
* The Phase two commit failed unretryable.
* description:Commit logic is failed and NOT retryable.
*/
BranchStatusPhasetwoCommitFailedUnretryable

/**
* The Phase two rollbacked.
* description:Rollback logic is successfully done at phase two.
*/
BranchStatusPhasetwoRollbacked

/**
* The Phase two rollback failed retryable.
* description:Rollback logic is failed but retryable.
*/
BranchStatusPhasetwoRollbackFailedRetryable

/**
* The Phase two rollback failed unretryable.
* description:Rollback logic is failed but NOT retryable.
*/
BranchStatusPhasetwoRollbackFailedUnretryable
)

func (s BranchStatus) String() string {
switch s {
case BranchStatusUnknown:
return "Unknown"
case BranchStatusRegistered:
return "Registered"
case BranchStatusPhaseoneDone:
return "PhaseoneDone"
case BranchStatusPhaseoneFailed:
return "PhaseoneFailed"
case BranchStatusPhaseoneTimeout:
return "PhaseoneTimeout"
case BranchStatusPhasetwoCommitted:
return "PhasetwoCommitted"
case BranchStatusPhasetwoCommitFailedRetryable:
return "PhasetwoCommitFailedRetryable"
case BranchStatusPhasetwoCommitFailedUnretryable:
return "CommitFailedUnretryable"
case BranchStatusPhasetwoRollbacked:
return "PhasetwoRollbacked"
case BranchStatusPhasetwoRollbackFailedRetryable:
return "RollbackFailedRetryable"
case BranchStatusPhasetwoRollbackFailedUnretryable:
return "RollbackFailedUnretryable"
default:
return fmt.Sprintf("%d", s)
}
}

+ 36
- 0
meta/branch_type.go View File

@@ -0,0 +1,36 @@
package meta

import "fmt"

type BranchType byte

const (
/**
* The At.
*/
// BranchType_AT Branch
BranchTypeAT BranchType = iota

/**
* The BranchType_TCC.
*/
BranchTypeTCC

/**
* The BranchType_SAGA.
*/
BranchTypeSAGA
)

func (t BranchType) String() string {
switch t {
case BranchTypeAT:
return "AT"
case BranchTypeTCC:
return "TCC"
case BranchTypeSAGA:
return "SAGA"
default:
return fmt.Sprintf("%d", t)
}
}

+ 142
- 0
meta/global_status.go View File

@@ -0,0 +1,142 @@
package meta

import "fmt"

type GlobalStatus int32

const (
/**
* Un known global status.
*/
// BranchStatus_Unknown
GlobalStatusUnknown GlobalStatus = iota

/**
* The GlobalStatus_Begin.
*/
// PHASE 1: can accept new branch registering.
GlobalStatusBegin

/**
* PHASE 2: Running Status: may be changed any time.
*/
// Committing.
GlobalStatusCommitting

/**
* The Commit retrying.
*/
// Retrying commit after a recoverable failure.
GlobalStatusCommitRetrying

/**
* Rollbacking global status.
*/
// Rollbacking
GlobalStatusRollbacking

/**
* The Rollback retrying.
*/
// Retrying rollback after a recoverable failure.
GlobalStatusRollbackRetrying

/**
* The Timeout rollbacking.
*/
// Rollbacking since timeout
GlobalStatusTimeoutRollbacking

/**
* The Timeout rollback retrying.
*/
// Retrying rollback (since timeout) after a recoverable failure.
GlobalStatusTimeoutRollbackRetrying

/**
* All branches can be async committed. The committing is NOT done yet, but it can be seen as committed for TM/RM
* client.
*/
GlobalStatusAsyncCommitting

/**
* PHASE 2: Final Status: will NOT change any more.
*/
// Finally: global transaction is successfully committed.
GlobalStatusCommitted

/**
* The Commit failed.
*/
// Finally: failed to commit
GlobalStatusCommitFailed

/**
* The Rollbacked.
*/
// Finally: global transaction is successfully rollbacked.
GlobalStatusRollbacked

/**
* The Rollback failed.
*/
// Finally: failed to rollback
GlobalStatusRollbackFailed

/**
* The Timeout rollbacked.
*/
// Finally: global transaction is successfully rollbacked since timeout.
GlobalStatusTimeoutRollbacked

/**
* The Timeout rollback failed.
*/
// Finally: failed to rollback since timeout
GlobalStatusTimeoutRollbackFailed

/**
* The Finished.
*/
// Not managed in session MAP any more
GlobalStatusFinished
)

func (s GlobalStatus) String() string {
switch s {
case GlobalStatusUnknown:
return "Unknown"
case GlobalStatusBegin:
return "Begin"
case GlobalStatusCommitting:
return "Committing"
case GlobalStatusCommitRetrying:
return "CommitRetrying"
case GlobalStatusRollbacking:
return "Rollbacking"
case GlobalStatusRollbackRetrying:
return "RollbackRetrying"
case GlobalStatusTimeoutRollbacking:
return "TimeoutRollbacking"
case GlobalStatusTimeoutRollbackRetrying:
return "TimeoutRollbackRetrying"
case GlobalStatusAsyncCommitting:
return "AsyncCommitting"
case GlobalStatusCommitted:
return "Committed"
case GlobalStatusCommitFailed:
return "CommitFailed"
case GlobalStatusRollbacked:
return "Rollbacked"
case GlobalStatusRollbackFailed:
return "RollbackFailed"
case GlobalStatusTimeoutRollbacked:
return "TimeoutRollbacked"
case GlobalStatusTimeoutRollbackFailed:
return "TimeoutRollbackFailed"
case GlobalStatusFinished:
return "Finished"
default:
return fmt.Sprintf("%d", s)
}
}

+ 110
- 0
meta/transaction_exception_code.go View File

@@ -0,0 +1,110 @@
package meta

type TransactionExceptionCode byte

const (
/**
* Unknown transaction exception code.
*/
TransactionExceptionCodeUnknown TransactionExceptionCode = iota

/**
* BeginFailed
*/
TransactionExceptionCodeBeginFailed

/**
* Lock key conflict transaction exception code.
*/
TransactionExceptionCodeLockKeyConflict

/**
* Io transaction exception code.
*/
IO

/**
* Branch rollback failed retriable transaction exception code.
*/
TransactionExceptionCodeBranchRollbackFailedRetriable

/**
* Branch rollback failed unretriable transaction exception code.
*/
TransactionExceptionCodeBranchRollbackFailedUnretriable

/**
* Branch register failed transaction exception code.
*/
TransactionExceptionCodeBranchRegisterFailed

/**
* Branch report failed transaction exception code.
*/
TransactionExceptionCodeBranchReportFailed

/**
* Lockable check failed transaction exception code.
*/
TransactionExceptionCodeLockableCheckFailed

/**
* Branch transaction not exist transaction exception code.
*/
TransactionExceptionCodeBranchTransactionNotExist

/**
* Global transaction not exist transaction exception code.
*/
TransactionExceptionCodeGlobalTransactionNotExist

/**
* Global transaction not active transaction exception code.
*/
TransactionExceptionCodeGlobalTransactionNotActive

/**
* Global transaction status invalid transaction exception code.
*/
TransactionExceptionCodeGlobalTransactionStatusInvalid

/**
* Failed to send branch commit request transaction exception code.
*/
TransactionExceptionCodeFailedToSendBranchCommitRequest

/**
* Failed to send branch rollback request transaction exception code.
*/
TransactionExceptionCodeFailedToSendBranchRollbackRequest

/**
* Failed to add branch transaction exception code.
*/
TransactionExceptionCodeFailedToAddBranch

/**
* Failed to lock global transaction exception code.
*/
TransactionExceptionCodeFailedLockGlobalTranscation

/**
* FailedWriteSession
*/
TransactionExceptionCodeFailedWriteSession

/**
* Failed to holder exception code
*/
FailedStore
)

type TransactionException struct {
Code TransactionExceptionCode
Message string
}

//Error 隐式继承 builtin.error 接口
func (e TransactionException) Error() string {
return "TransactionException: " + e.Message
}

+ 33
- 0
meta/transaction_role.go View File

@@ -0,0 +1,33 @@
package meta

import "fmt"

type TransactionRole byte

const (
/**
* tm
*/
TMROLE TransactionRole = iota
/**
* rm
*/
RMROLE
/**
* server
*/
SERVERROLE
)

func (r TransactionRole) String() string {
switch r {
case TMROLE:
return "TMROLE"
case RMROLE:
return "RMROLE"
case SERVERROLE:
return "SERVERROLE"
default:
return fmt.Sprintf("%d", r)
}
}

+ 28
- 0
model/resource.go View File

@@ -0,0 +1,28 @@
package model

import "github.com/dk-lockdown/seata-golang/meta"

type IResource interface {
/**
* Get the resource group id.
* e.g. master and slave data-source should be with the same resource group id.
*
* @return resource group id.
*/
getResourceGroupId() string

/**
* Get the resource id.
* e.g. url of a data-source could be the id of the db data-source resource.
*
* @return resource id.
*/
getResourceId() string

/**
* get resource type, BranchType_AT, BranchType_TCC, BranchType_SAGA and XA
*
* @return
*/
getBranchType() meta.BranchType
}

+ 69
- 0
model/set.go View File

@@ -0,0 +1,69 @@
package model

import "sync"

type Set struct {
m map[string]bool
sync.RWMutex
}

func NewSet() *Set {
return &Set{
m: make(map[string]bool),
}
}

// Add add
func (s *Set) Add(item string) {
s.Lock()
defer s.Unlock()
s.m[item] = true
}

// Remove deletes the specified item from the map
func (s *Set) Remove(item string) {
s.Lock()
defer s.Unlock()
delete(s.m, item)
}

// Has looks for the existence of an item
func (s *Set) Has(item string) bool {
s.RLock()
defer s.RUnlock()
_, ok := s.m[item]
return ok
}

// Len returns the number of items in a set.
func (s *Set) Len() int {
return len(s.List())
}

// Clear removes all items from the set
func (s *Set) Clear() {
s.Lock()
defer s.Unlock()
s.m = make(map[string]bool)
}

// IsEmpty checks for emptiness
func (s *Set) IsEmpty() bool {
if s.Len() == 0 {
return true
}
return false
}

// Set returns a slice of all items
func (s *Set) List() []string {
s.RLock()
defer s.RUnlock()
list := make([]string, 0)
for item := range s.m {
list = append(list, item)
}
return list
}



+ 0
- 107
pkg/client/client.go View File

@@ -1,107 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package client

import (
"sync"

"seata.apache.org/seata-go/pkg/datasource"
at "seata.apache.org/seata-go/pkg/datasource/sql"
"seata.apache.org/seata-go/pkg/datasource/sql/exec/config"
"seata.apache.org/seata-go/pkg/discovery"
"seata.apache.org/seata-go/pkg/integration"
remoteConfig "seata.apache.org/seata-go/pkg/remoting/config"
"seata.apache.org/seata-go/pkg/remoting/getty"
"seata.apache.org/seata-go/pkg/remoting/processor/client"
"seata.apache.org/seata-go/pkg/rm"
"seata.apache.org/seata-go/pkg/rm/tcc"
"seata.apache.org/seata-go/pkg/tm"
"seata.apache.org/seata-go/pkg/util/log"
)

// Init seata client client
func Init() {
InitPath("")
}

// InitPath init client with config path
func InitPath(configFilePath string) {
cfg := LoadPath(configFilePath)
initRegistry(cfg)
initRmClient(cfg)
initTmClient(cfg)
initDatasource()
}

var (
onceInitTmClient sync.Once
onceInitRmClient sync.Once
onceInitDatasource sync.Once
onceInitRegistry sync.Once
)

// InitTmClient init client tm client
func initTmClient(cfg *Config) {
onceInitTmClient.Do(func() {
tm.InitTm(cfg.ClientConfig.TmConfig)
})
}

// initRemoting init remoting
func initRemoting(cfg *Config) {
seataConfig := remoteConfig.SeataConfig{
ApplicationID: cfg.ApplicationID,
TxServiceGroup: cfg.TxServiceGroup,
ServiceVgroupMapping: cfg.ServiceConfig.VgroupMapping,
ServiceGrouplist: cfg.ServiceConfig.Grouplist,
LoadBalanceType: cfg.GettyConfig.LoadBalanceType,
}

getty.InitGetty(&cfg.GettyConfig, &seataConfig)
}

// InitRmClient init client rm client
func initRmClient(cfg *Config) {
onceInitRmClient.Do(func() {
log.Init()
initRemoting(cfg)
rm.InitRm(rm.RmConfig{
Config: cfg.ClientConfig.RmConfig,
ApplicationID: cfg.ApplicationID,
TxServiceGroup: cfg.TxServiceGroup,
})
config.Init(cfg.ClientConfig.RmConfig.LockConfig)
client.RegisterProcessor()
integration.Init()
tcc.InitTCC(cfg.TCCConfig.FenceConfig)
at.InitAT(cfg.ClientConfig.UndoConfig, cfg.AsyncWorkerConfig)
at.InitXA(cfg.ClientConfig.XaConfig)
})
}

func initDatasource() {
onceInitDatasource.Do(func() {
datasource.Init()
})
}

func initRegistry(cfg *Config) {
onceInitRegistry.Do(func() {
discovery.InitRegistry(&cfg.ServiceConfig, &cfg.RegistryConfig)
})
}

+ 0
- 254
pkg/client/config.go View File

@@ -1,254 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package client

import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/knadh/koanf"
"github.com/knadh/koanf/parsers/json"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/rawbytes"

"seata.apache.org/seata-go/pkg/discovery"

"seata.apache.org/seata-go/pkg/datasource/sql"
"seata.apache.org/seata-go/pkg/datasource/sql/undo"
remoteConfig "seata.apache.org/seata-go/pkg/remoting/config"
"seata.apache.org/seata-go/pkg/rm"
"seata.apache.org/seata-go/pkg/rm/tcc"
"seata.apache.org/seata-go/pkg/tm"
"seata.apache.org/seata-go/pkg/util/flagext"
)

const (
configFileEnvKey = "SEATA_GO_CONFIG_PATH"
configPrefix = "seata"
)

const (
jsonSuffix = "json"
tomlSuffix = "toml"
yamlSuffix = "yaml"
ymlSuffix = "yml"
)

type ClientConfig struct {
TmConfig tm.TmConfig `yaml:"tm" json:"tm,omitempty" koanf:"tm"`
RmConfig rm.Config `yaml:"rm" json:"rm,omitempty" koanf:"rm"`
UndoConfig undo.Config `yaml:"undo" json:"undo,omitempty" koanf:"undo"`
XaConfig sql.XAConfig `yaml:"xa" json:"xa" koanf:"xa"`
}

func (c *ClientConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
c.TmConfig.RegisterFlagsWithPrefix(prefix+".tm", f)
c.RmConfig.RegisterFlagsWithPrefix(prefix+".rm", f)
c.UndoConfig.RegisterFlagsWithPrefix(prefix+".undo", f)
c.XaConfig.RegisterFlagsWithPrefix(prefix+".xa", f)
}

type Config struct {
Enabled bool `yaml:"enabled" json:"enabled,omitempty" koanf:"enabled"`
ApplicationID string `yaml:"application-id" json:"application-id,omitempty" koanf:"application-id"`
TxServiceGroup string `yaml:"tx-service-group" json:"tx-service-group,omitempty" koanf:"tx-service-group"`
AccessKey string `yaml:"access-key" json:"access-key,omitempty" koanf:"access-key"`
SecretKey string `yaml:"secret-key" json:"secret-key,omitempty" koanf:"secret-key"`
EnableAutoDataSourceProxy bool `yaml:"enable-auto-data-source-proxy" json:"enable-auto-data-source-proxy,omitempty" koanf:"enable-auto-data-source-proxy"`
DataSourceProxyMode string `yaml:"data-source-proxy-mode" json:"data-source-proxy-mode,omitempty" koanf:"data-source-proxy-mode"`

AsyncWorkerConfig sql.AsyncWorkerConfig `yaml:"async" json:"async" koanf:"async"`
TCCConfig tcc.Config `yaml:"tcc" json:"tcc" koanf:"tcc"`
ClientConfig ClientConfig `yaml:"client" json:"client" koanf:"client"`
GettyConfig remoteConfig.Config `yaml:"getty" json:"getty" koanf:"getty"`
TransportConfig remoteConfig.TransportConfig `yaml:"transport" json:"transport" koanf:"transport"`
ServiceConfig discovery.ServiceConfig `yaml:"service" json:"service" koanf:"service"`
RegistryConfig discovery.RegistryConfig `yaml:"registry" json:"registry" koanf:"registry"`
}

func (c *Config) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&c.Enabled, "enabled", true, "Whether enable auto configuration.")
f.StringVar(&c.ApplicationID, "application-id", "seata-go", "Application id.")
f.StringVar(&c.TxServiceGroup, "tx-service-group", "default_tx_group", "Transaction service group.")
f.StringVar(&c.AccessKey, "access-key", "", "Used for aliyun accessKey.")
f.StringVar(&c.SecretKey, "secret-key", "", "Used for aliyun secretKey.")
f.BoolVar(&c.EnableAutoDataSourceProxy, "enable-auto-data-source-proxy", true, "Whether enable auto proxying of datasource bean.")
f.StringVar(&c.DataSourceProxyMode, "data-source-proxy-mode", "AT", "Data source proxy mode.")

c.AsyncWorkerConfig.RegisterFlagsWithPrefix("async-worker", f)
c.TCCConfig.RegisterFlagsWithPrefix("tcc", f)
c.ClientConfig.RegisterFlagsWithPrefix("client", f)
c.GettyConfig.RegisterFlagsWithPrefix("getty", f)
c.TransportConfig.RegisterFlagsWithPrefix("transport", f)
c.RegistryConfig.RegisterFlagsWithPrefix("registry", f)
c.ServiceConfig.RegisterFlagsWithPrefix("service", f)
}

type loaderConf struct {
suffix string // loaderConf file extension default yaml
path string // loaderConf file path default ./conf/seatago.yaml
delim string // loaderConf file delim default .
bytes []byte // config bytes
name string // config file name
}

// Load parse config from user config path
func LoadPath(configFilePath string) *Config {
if configFilePath == "" {
configFilePath = os.Getenv(configFileEnvKey)
if configFilePath == "" {
panic("system variable SEATA_GO_CONFIG_PATH is empty")
}
}

var cfg Config
// This sets default values from flags to the config.
// It needs to be called before parsing the config file!
flagext.RegisterFlags(&cfg)

conf := newLoaderConf(configFilePath)
koan := getConfigResolver(conf)
if err := koan.UnmarshalWithConf(configPrefix, &cfg, koanf.UnmarshalConf{Tag: yamlSuffix}); err != nil {
panic(err)
}
return &cfg
}

// Load parse config from json bytes
func LoadJson(bytes []byte) *Config {
var cfg Config
// This sets default values from flags to the config.
// It needs to be called before parsing the config file!
flagext.RegisterFlags(&cfg)

koan := getJsonConfigResolver(bytes)
if err := koan.Unmarshal("", &cfg); err != nil {
panic(err)
}
return &cfg
}

// getJsonConfigResolver get json config resolver
func getJsonConfigResolver(bytes []byte) *koanf.Koanf {
k := koanf.New(".")
if err := k.Load(rawbytes.Provider(bytes), json.Parser()); err != nil {
panic(err)
}
return k
}

// resolverFilePath resolver file path
// eg: give a ./conf/seatago.yaml return seatago and yaml
func resolverFilePath(path string) (name, suffix string) {
paths := strings.Split(path, "/")
fileName := strings.Split(paths[len(paths)-1], ".")
if len(fileName) < 2 {
return fileName[0], yamlSuffix
}
return fileName[0], fileName[1]
}

// getConfigResolver get config resolver
func getConfigResolver(conf *loaderConf) *koanf.Koanf {
var (
k *koanf.Koanf
err error
)
if len(conf.suffix) <= 0 {
conf.suffix = yamlSuffix
}
if len(conf.delim) <= 0 {
conf.delim = "."
}
bytes := conf.bytes
if len(bytes) <= 0 {
panic(fmt.Errorf("bytes is nil,please set bytes or file path"))
}
k = koanf.New(conf.delim)

switch conf.suffix {
case yamlSuffix, ymlSuffix:
err = k.Load(rawbytes.Provider(bytes), yaml.Parser())
case jsonSuffix:
err = k.Load(rawbytes.Provider(bytes), json.Parser())
case tomlSuffix:
err = k.Load(rawbytes.Provider(bytes), toml.Parser())
default:
err = fmt.Errorf("no support %s file suffix", conf.suffix)
}

if err != nil {
panic(err)
}
return k
}

func newLoaderConf(configFilePath string) *loaderConf {
name, suffix := resolverFilePath(configFilePath)
conf := &loaderConf{
suffix: suffix,
path: absolutePath(configFilePath),
delim: ".",
name: name,
}

if len(conf.bytes) <= 0 {
if bytes, err := ioutil.ReadFile(conf.path); err != nil {
panic(err)
} else {
conf.bytes = bytes
}
}
return conf
}

// absolutePath get absolut path
func absolutePath(inPath string) string {
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
inPath = userHomeDir() + inPath[5:]
}

if filepath.IsAbs(inPath) {
return filepath.Clean(inPath)
}

p, err := filepath.Abs(inPath)
if err == nil {
return filepath.Clean(p)
}

return ""
}

// userHomeDir get gopath
func userHomeDir() string {
if runtime.GOOS == "windows" {
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
if home == "" {
home = os.Getenv("USERPROFILE")
}
return home
}
return os.Getenv("HOME")
}

+ 0
- 226
pkg/client/config_test.go View File

@@ -1,226 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package client

import (
"flag"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestLoadPath(t *testing.T) {
cfg := LoadPath("../../testdata/conf/seatago.yml")
assert.NotNil(t, cfg)
assert.Equal(t, true, cfg.Enabled)
assert.Equal(t, "applicationName", cfg.ApplicationID)
assert.Equal(t, "default_tx_group", cfg.TxServiceGroup)
assert.Equal(t, "aliyunAccessKey", cfg.AccessKey)
assert.Equal(t, "aliyunSecretKey", cfg.SecretKey)
assert.Equal(t, true, cfg.EnableAutoDataSourceProxy)
assert.Equal(t, "AT", cfg.DataSourceProxyMode)

assert.NotNil(t, cfg.TCCConfig)
assert.NotNil(t, cfg.TCCConfig.FenceConfig)
assert.Equal(t, false, cfg.TCCConfig.FenceConfig.Enable)
assert.Equal(t, "root:12345678@tcp(127.0.0.1:3306)/seata_client1?charset=utf8&parseTime=True", cfg.TCCConfig.FenceConfig.Url)
assert.Equal(t, "tcc_fence_log_test", cfg.TCCConfig.FenceConfig.LogTableName)
assert.Equal(t, time.Second*60, cfg.TCCConfig.FenceConfig.CleanPeriod)

assert.NotNil(t, cfg.ClientConfig)
assert.NotNil(t, cfg.ClientConfig.TmConfig)
assert.Equal(t, 5, cfg.ClientConfig.TmConfig.CommitRetryCount)
assert.Equal(t, 5, cfg.ClientConfig.TmConfig.RollbackRetryCount)
assert.Equal(t, time.Second*60, cfg.ClientConfig.TmConfig.DefaultGlobalTransactionTimeout)
assert.Equal(t, false, cfg.ClientConfig.TmConfig.DegradeCheck)
assert.Equal(t, 2000, cfg.ClientConfig.TmConfig.DegradeCheckPeriod)
assert.Equal(t, time.Second*10, cfg.ClientConfig.TmConfig.DegradeCheckAllowTimes)
assert.Equal(t, -2147482648, cfg.ClientConfig.TmConfig.InterceptorOrder)

assert.Equal(t, 10000, cfg.ClientConfig.RmConfig.AsyncCommitBufferLimit)
assert.Equal(t, 5, cfg.ClientConfig.RmConfig.ReportRetryCount)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.TableMetaCheckEnable)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.ReportSuccessEnable)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.SagaBranchRegisterEnable)
assert.Equal(t, "fastjson", cfg.ClientConfig.RmConfig.SagaJsonParser)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.SagaCompensatePersistModeUpdate)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.SagaRetryPersistModeUpdate)
assert.Equal(t, -2147482648, cfg.ClientConfig.RmConfig.TccActionInterceptorOrder)
assert.Equal(t, "druid", cfg.ClientConfig.RmConfig.SqlParserType)
assert.Equal(t, 30*time.Second, cfg.ClientConfig.RmConfig.LockConfig.RetryInterval)
assert.Equal(t, 10, cfg.ClientConfig.RmConfig.LockConfig.RetryTimes)
assert.Equal(t, true, cfg.ClientConfig.RmConfig.LockConfig.RetryPolicyBranchRollbackOnConflict)

assert.NotNil(t, cfg.ClientConfig.UndoConfig)
assert.Equal(t, true, cfg.ClientConfig.UndoConfig.DataValidation)
assert.Equal(t, "json", cfg.ClientConfig.UndoConfig.LogSerialization)
assert.Equal(t, "undo_log", cfg.ClientConfig.UndoConfig.LogTable)
assert.Equal(t, true, cfg.ClientConfig.UndoConfig.OnlyCareUpdateColumns)
assert.NotNil(t, cfg.ClientConfig.UndoConfig.CompressConfig)
assert.Equal(t, true, cfg.ClientConfig.UndoConfig.CompressConfig.Enable)
assert.Equal(t, "zip", cfg.ClientConfig.UndoConfig.CompressConfig.Type)
assert.Equal(t, "64k", cfg.ClientConfig.UndoConfig.CompressConfig.Threshold)

assert.NotNil(t, cfg.GettyConfig)
assert.NotNil(t, cfg.GettyConfig.SessionConfig)
assert.Equal(t, 0, cfg.GettyConfig.ReconnectInterval)
assert.Equal(t, 1, cfg.GettyConfig.ConnectionNum)
assert.Equal(t, false, cfg.GettyConfig.SessionConfig.CompressEncoding)
assert.Equal(t, true, cfg.GettyConfig.SessionConfig.TCPNoDelay)
assert.Equal(t, true, cfg.GettyConfig.SessionConfig.TCPKeepAlive)
assert.Equal(t, time.Minute*2, cfg.GettyConfig.SessionConfig.KeepAlivePeriod)
assert.Equal(t, 262144, cfg.GettyConfig.SessionConfig.TCPRBufSize)
assert.Equal(t, 65536, cfg.GettyConfig.SessionConfig.TCPWBufSize)
assert.Equal(t, time.Second, cfg.GettyConfig.SessionConfig.TCPReadTimeout)
assert.Equal(t, time.Second*5, cfg.GettyConfig.SessionConfig.TCPWriteTimeout)
assert.Equal(t, time.Second, cfg.GettyConfig.SessionConfig.WaitTimeout)
assert.Equal(t, 16498688, cfg.GettyConfig.SessionConfig.MaxMsgLen)
assert.Equal(t, "client_test", cfg.GettyConfig.SessionConfig.SessionName)
assert.Equal(t, time.Second, cfg.GettyConfig.SessionConfig.CronPeriod)

assert.NotNil(t, cfg.TransportConfig)
assert.NotNil(t, cfg.TransportConfig.ShutdownConfig)
assert.Equal(t, time.Second*3, cfg.TransportConfig.ShutdownConfig.Wait)
assert.Equal(t, "TCP", cfg.TransportConfig.Type)
assert.Equal(t, "NIO", cfg.TransportConfig.Server)
assert.Equal(t, true, cfg.TransportConfig.Heartbeat)
assert.Equal(t, "seata", cfg.TransportConfig.Serialization)
assert.Equal(t, "none", cfg.TransportConfig.Compressor)
assert.Equal(t, false, cfg.TransportConfig.EnableTmClientBatchSendRequest)
assert.Equal(t, true, cfg.TransportConfig.EnableRmClientBatchSendRequest)
assert.Equal(t, time.Second*30, cfg.TransportConfig.RPCRmRequestTimeout)
assert.Equal(t, time.Second*30, cfg.TransportConfig.RPCTmRequestTimeout)

assert.NotNil(t, cfg.ServiceConfig)
assert.Equal(t, false, cfg.ServiceConfig.EnableDegrade)
assert.Equal(t, false, cfg.ServiceConfig.DisableGlobalTransaction)
assert.Equal(t, "default", cfg.ServiceConfig.VgroupMapping["default_tx_group"])
assert.Equal(t, "127.0.0.1:8091", cfg.ServiceConfig.Grouplist["default"])

assert.NotNil(t, cfg.RegistryConfig)
assert.Equal(t, "file", cfg.RegistryConfig.Type)
assert.Equal(t, "seatago.yml", cfg.RegistryConfig.File.Name)
assert.Equal(t, "seata-server", cfg.RegistryConfig.Nacos.Application)
assert.Equal(t, "127.0.0.1:8848", cfg.RegistryConfig.Nacos.ServerAddr)
assert.Equal(t, "SEATA_GROUP", cfg.RegistryConfig.Nacos.Group)
assert.Equal(t, "test-namespace", cfg.RegistryConfig.Nacos.Namespace)
assert.Equal(t, "test-username", cfg.RegistryConfig.Nacos.Username)
assert.Equal(t, "test-password", cfg.RegistryConfig.Nacos.Password)
assert.Equal(t, "test-access-key", cfg.RegistryConfig.Nacos.AccessKey)
assert.Equal(t, "test-secret-key", cfg.RegistryConfig.Nacos.SecretKey)
assert.Equal(t, "default", cfg.RegistryConfig.Etcd3.Cluster)
assert.Equal(t, "http://localhost:2379", cfg.RegistryConfig.Etcd3.ServerAddr)

// reset flag.CommandLine
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
}

func TestLoadJson(t *testing.T) {
confJson := `{"enabled":false,"application-id":"application_test","tx-service-group":"default_tx_group","access-key":"test","secret-key":"test","enable-auto-data-source-proxy":false,"data-source-proxy-mode":"AT","client":{"rm":{"async-commit-buffer-limit":10000,"report-retry-count":5,"table-meta-check-enable":false,"report-success-enable":false,"saga-branch-register-enable":false,"saga-json-parser":"fastjson","saga-retry-persist-mode-update":false,"saga-compensate-persist-mode-update":false,"tcc-action-interceptor-order":-2147482648,"sql-parser-type":"druid","lock":{"retry-interval":"30s","retry-times":10,"retry-policy-branch-rollback-on-conflict":true}},"tm":{"commit-retry-count":5,"rollback-retry-count":5,"default-global-transaction-timeout":"60s","degrade-check":false,"degrade-check-period":2000,"degrade-check-allow-times":"10s","interceptor-order":-2147482648},"undo":{"data-validation":false,"log-serialization":"jackson222","only-care-update-columns":false,"log-table":"undo_log333","compress":{"enable":false,"type":"zip111","threshold":"128k"}}},"tcc":{"fence":{"enable":false,"url":"root:12345678@tcp(127.0.0.1:3306)/seata_client1?charset=utf8&parseTime=True","log-table-name":"tcc_fence_log_test2","clean-period":80000000000}},"getty":{"reconnect-interval":1,"connection-num":10,"session":{"compress-encoding":true,"tcp-no-delay":false,"tcp-keep-alive":false,"keep-alive-period":"120s","tcp-r-buf-size":261120,"tcp-w-buf-size":32768,"tcp-read-timeout":"2s","tcp-write-timeout":"8s","wait-timeout":"2s","max-msg-len":261120,"session-name":"client_test","cron-period":"2s"}},"transport":{"shutdown":{"wait":"3s"},"type":"TCP","server":"NIO","heartbeat":true,"serialization":"seata","compressor":"none"," enable-tm-client-batch-send-request":false,"enable-rm-client-batch-send-request":true,"rpc-rm-request-timeout":"30s","rpc-tm-request-timeout":"30s"},"service":{"enable-degrade":true,"disable-global-transaction":true,"vgroup-mapping":{"default_tx_group":"default_test"},"grouplist":{"default":"127.0.0.1:8092"}}}`
cfg := LoadJson([]byte(confJson))
assert.NotNil(t, cfg)
assert.Equal(t, false, cfg.Enabled)
assert.Equal(t, "application_test", cfg.ApplicationID)
assert.Equal(t, "default_tx_group", cfg.TxServiceGroup)
assert.Equal(t, "test", cfg.AccessKey)
assert.Equal(t, "test", cfg.SecretKey)
assert.Equal(t, false, cfg.EnableAutoDataSourceProxy)
assert.Equal(t, "AT", cfg.DataSourceProxyMode)

assert.Equal(t, 10000, cfg.ClientConfig.RmConfig.AsyncCommitBufferLimit)
assert.Equal(t, 5, cfg.ClientConfig.RmConfig.ReportRetryCount)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.TableMetaCheckEnable)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.ReportSuccessEnable)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.SagaBranchRegisterEnable)
assert.Equal(t, "fastjson", cfg.ClientConfig.RmConfig.SagaJsonParser)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.SagaCompensatePersistModeUpdate)
assert.Equal(t, false, cfg.ClientConfig.RmConfig.SagaRetryPersistModeUpdate)
assert.Equal(t, -2147482648, cfg.ClientConfig.RmConfig.TccActionInterceptorOrder)
assert.Equal(t, "druid", cfg.ClientConfig.RmConfig.SqlParserType)
assert.Equal(t, 30*time.Second, cfg.ClientConfig.RmConfig.LockConfig.RetryInterval)
assert.Equal(t, 10, cfg.ClientConfig.RmConfig.LockConfig.RetryTimes)
assert.Equal(t, true, cfg.ClientConfig.RmConfig.LockConfig.RetryPolicyBranchRollbackOnConflict)

assert.NotNil(t, cfg.ClientConfig.UndoConfig)
assert.Equal(t, false, cfg.ClientConfig.UndoConfig.DataValidation)
assert.Equal(t, "jackson222", cfg.ClientConfig.UndoConfig.LogSerialization)
assert.Equal(t, "undo_log333", cfg.ClientConfig.UndoConfig.LogTable)
assert.Equal(t, false, cfg.ClientConfig.UndoConfig.OnlyCareUpdateColumns)
assert.NotNil(t, cfg.ClientConfig.UndoConfig.CompressConfig)
assert.Equal(t, false, cfg.ClientConfig.UndoConfig.CompressConfig.Enable)
assert.Equal(t, "zip111", cfg.ClientConfig.UndoConfig.CompressConfig.Type)
assert.Equal(t, "128k", cfg.ClientConfig.UndoConfig.CompressConfig.Threshold)

assert.NotNil(t, cfg.TCCConfig)
assert.NotNil(t, cfg.TCCConfig.FenceConfig)
assert.Equal(t, false, cfg.TCCConfig.FenceConfig.Enable)
assert.Equal(t, "root:12345678@tcp(127.0.0.1:3306)/seata_client1?charset=utf8&parseTime=True", cfg.TCCConfig.FenceConfig.Url)
assert.Equal(t, "tcc_fence_log_test2", cfg.TCCConfig.FenceConfig.LogTableName)
assert.Equal(t, time.Second*80, cfg.TCCConfig.FenceConfig.CleanPeriod)

assert.NotNil(t, cfg.ClientConfig)
assert.NotNil(t, cfg.ClientConfig.TmConfig)
assert.Equal(t, 5, cfg.ClientConfig.TmConfig.CommitRetryCount)
assert.Equal(t, 5, cfg.ClientConfig.TmConfig.RollbackRetryCount)
assert.Equal(t, time.Second*60, cfg.ClientConfig.TmConfig.DefaultGlobalTransactionTimeout)
assert.Equal(t, false, cfg.ClientConfig.TmConfig.DegradeCheck)
assert.Equal(t, 2000, cfg.ClientConfig.TmConfig.DegradeCheckPeriod)
assert.Equal(t, time.Second*10, cfg.ClientConfig.TmConfig.DegradeCheckAllowTimes)
assert.Equal(t, -2147482648, cfg.ClientConfig.TmConfig.InterceptorOrder)

assert.NotNil(t, cfg.GettyConfig)
assert.NotNil(t, cfg.GettyConfig.SessionConfig)
assert.Equal(t, 1, cfg.GettyConfig.ReconnectInterval)
assert.Equal(t, 10, cfg.GettyConfig.ConnectionNum)
assert.Equal(t, true, cfg.GettyConfig.SessionConfig.CompressEncoding)
assert.Equal(t, false, cfg.GettyConfig.SessionConfig.TCPNoDelay)
assert.Equal(t, false, cfg.GettyConfig.SessionConfig.TCPKeepAlive)
assert.Equal(t, time.Minute*2, cfg.GettyConfig.SessionConfig.KeepAlivePeriod)
assert.Equal(t, 261120, cfg.GettyConfig.SessionConfig.TCPRBufSize)
assert.Equal(t, 32768, cfg.GettyConfig.SessionConfig.TCPWBufSize)
assert.Equal(t, time.Second*2, cfg.GettyConfig.SessionConfig.TCPReadTimeout)
assert.Equal(t, time.Second*8, cfg.GettyConfig.SessionConfig.TCPWriteTimeout)
assert.Equal(t, time.Second*2, cfg.GettyConfig.SessionConfig.WaitTimeout)
assert.Equal(t, 261120, cfg.GettyConfig.SessionConfig.MaxMsgLen)
assert.Equal(t, "client_test", cfg.GettyConfig.SessionConfig.SessionName)
assert.Equal(t, time.Second*2, cfg.GettyConfig.SessionConfig.CronPeriod)

assert.NotNil(t, cfg.TransportConfig)
assert.NotNil(t, cfg.TransportConfig.ShutdownConfig)
assert.Equal(t, time.Second*3, cfg.TransportConfig.ShutdownConfig.Wait)
assert.Equal(t, "TCP", cfg.TransportConfig.Type)
assert.Equal(t, "NIO", cfg.TransportConfig.Server)
assert.Equal(t, true, cfg.TransportConfig.Heartbeat)
assert.Equal(t, "seata", cfg.TransportConfig.Serialization)
assert.Equal(t, "none", cfg.TransportConfig.Compressor)
assert.Equal(t, false, cfg.TransportConfig.EnableTmClientBatchSendRequest)
assert.Equal(t, true, cfg.TransportConfig.EnableRmClientBatchSendRequest)
assert.Equal(t, time.Second*30, cfg.TransportConfig.RPCRmRequestTimeout)
assert.Equal(t, time.Second*30, cfg.TransportConfig.RPCTmRequestTimeout)

assert.NotNil(t, cfg.ServiceConfig)
assert.Equal(t, true, cfg.ServiceConfig.EnableDegrade)
assert.Equal(t, true, cfg.ServiceConfig.DisableGlobalTransaction)
assert.Equal(t, "default_test", cfg.ServiceConfig.VgroupMapping["default_tx_group"])
assert.Equal(t, "127.0.0.1:8092", cfg.ServiceConfig.Grouplist["default"])

// reset flag.CommandLine
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
}

+ 0
- 18
pkg/compressor/7z_compress.go View File

@@ -1,18 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

+ 0
- 67
pkg/compressor/bzip2_compress.go View File

@@ -1,67 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"bytes"
"io/ioutil"

"github.com/dsnet/compress/bzip2"
)

type Bzip2 struct {
}

// Compress Bzip2 compress
func (g *Bzip2) Compress(b []byte) ([]byte, error) {
var buffer bytes.Buffer
gz, err := bzip2.NewWriter(&buffer, &bzip2.WriterConfig{Level: bzip2.DefaultCompression})
if err != nil {
return nil, err
}

if _, err := gz.Write(b); err != nil {
return nil, err
}

if err := gz.Close(); err != nil {
return nil, err
}

return buffer.Bytes(), nil
}

// Decompress Bzip2 decompress
func (g *Bzip2) Decompress(in []byte) ([]byte, error) {
reader, err := bzip2.NewReader(bytes.NewReader(in), nil)
if err != nil {
return nil, err
}
if err = reader.Close(); err != nil {
return nil, err
}
output, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
return output, nil
}

func (g *Bzip2) GetCompressorType() CompressorType {
return CompressorBzip2
}

+ 0
- 40
pkg/compressor/bzip2_compress_test.go View File

@@ -1,40 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestBzip2Compress(t *testing.T) {
str := strings.Repeat(" bzip2 ", 100)

b := &Bzip2{}
compressRes, err := b.Compress([]byte(str))
assert.NoError(t, err)
t.Logf("compress res: %v", string(compressRes))

decompressRes, err := b.Decompress(compressRes)
assert.NoError(t, err)
assert.Equal(t, str, string(decompressRes))

t.Logf("decompress res: %v", string(decompressRes))
}

+ 0
- 24
pkg/compressor/compressor.go View File

@@ -1,24 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

type Compressor interface {
Compress([]byte) ([]byte, error)
Decompress([]byte) ([]byte, error)
GetCompressorType() CompressorType
}

+ 0
- 53
pkg/compressor/compressor_type.go View File

@@ -1,53 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

type CompressorType string

const (
// "None" means no compressor is used
CompressorNone CompressorType = "None"
CompressorGzip CompressorType = "Gzip"
CompressorZip CompressorType = "Zip"
CompressorSevenz CompressorType = "Sevenz"
CompressorBzip2 CompressorType = "Bzip2"
CompressorLz4 CompressorType = "Lz4"
CompressorDeflate CompressorType = "Deflate"
CompressorZstd CompressorType = "Zstd"
)

func (c CompressorType) GetCompressor() Compressor {
switch c {
case CompressorNone:
return &NoneCompressor{}
case CompressorGzip:
return &Gzip{}
case CompressorZip:
return &Zip{}
case CompressorBzip2:
return &Bzip2{}
case CompressorLz4:
return &Lz4{}
case CompressorZstd:
return &Zstd{}
case CompressorDeflate:
return &DeflateCompress{}
default:
return &NoneCompressor{}
}
}

+ 0
- 51
pkg/compressor/deflate_compress.go View File

@@ -1,51 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"bytes"
"compress/flate"
"io"

"seata.apache.org/seata-go/pkg/util/log"
)

type DeflateCompress struct{}

func (*DeflateCompress) Compress(data []byte) ([]byte, error) {
var buf bytes.Buffer
fw, err := flate.NewWriter(&buf, flate.BestCompression)
if err != nil {
log.Error(err)
return nil, err
}
defer fw.Close()
fw.Write(data)
fw.Flush()
return buf.Bytes(), nil
}

func (*DeflateCompress) Decompress(data []byte) ([]byte, error) {
fr := flate.NewReader(bytes.NewBuffer(data))
defer fr.Close()
return io.ReadAll(fr)
}

func (*DeflateCompress) GetCompressorType() CompressorType {
return CompressorDeflate
}

+ 0
- 53
pkg/compressor/deflate_compress_test.go View File

@@ -1,53 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestDeflateCompress(t *testing.T) {
ts := []struct {
text string
}{
{
text: "Don't communicate by sharing memory, share memory by communicating.",
},
{
text: "Concurrency is not parallelism.",
},
{
text: "The bigger the interface, the weaker the abstraction.",
},
{
text: "Documentation is for users.",
},
}

dc := &DeflateCompress{}
assert.EqualValues(t, CompressorDeflate, dc.GetCompressorType())

for _, s := range ts {
var data []byte = []byte(s.text)
dataCompressed, _ := dc.Compress(data)
ret, _ := dc.Decompress(dataCompressed)
assert.EqualValues(t, s.text, string(ret))
}
}

+ 0
- 66
pkg/compressor/gzip_compress.go View File

@@ -1,66 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"bytes"
"compress/gzip"
"io/ioutil"
)

type Gzip struct {
}

// Compress gzip compress
func (g *Gzip) Compress(b []byte) ([]byte, error) {
var buffer bytes.Buffer

gz := gzip.NewWriter(&buffer)

if _, err := gz.Write(b); err != nil {
return nil, err
}

if err := gz.Flush(); err != nil {
return nil, err
}

if err := gz.Close(); err != nil {
return nil, err
}

return buffer.Bytes(), nil
}

// Decompress gzip decompress
func (g *Gzip) Decompress(in []byte) ([]byte, error) {
reader, err := gzip.NewReader(bytes.NewReader(in))
if err != nil {
return nil, err
}

if err = reader.Close(); err != nil {
return nil, err
}

return ioutil.ReadAll(reader)
}

func (g *Gzip) GetCompressorType() CompressorType {
return CompressorGzip
}

+ 0
- 40
pkg/compressor/gzip_compress_test.go View File

@@ -1,40 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGzipCompress(t *testing.T) {
str := "test"

g := &Gzip{}

compressRes, err := g.Compress([]byte(str))
assert.NoError(t, err)
t.Logf("compress res: %v", string(compressRes))

decompressRes, err := g.Decompress(compressRes)
assert.NoError(t, err)
t.Logf("decompress res: %v", string(decompressRes))

assert.Equal(t, str, string(decompressRes))
}

+ 0
- 57
pkg/compressor/lz4_compress.go View File

@@ -1,57 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"fmt"

"github.com/pierrec/lz4/v4"
)

type Lz4 struct {
}

func (l *Lz4) Compress(data []byte) ([]byte, error) {

buffer := make([]byte, lz4.CompressBlockBound(len(data)))

var compressor lz4.Compressor

n, err := compressor.CompressBlock(data, buffer)
if err != nil {
return nil, err
}
if n >= len(data) {
return nil, fmt.Errorf("`%s` is not compressible", string(data))
}

return buffer[:n], nil
}

func (l *Lz4) Decompress(in []byte) ([]byte, error) {
out := make([]byte, 100*len(in))
n, err := lz4.UncompressBlock(in, out)
if err != nil {
return nil, err
}
return out[:n], nil
}

func (l *Lz4) GetCompressorType() CompressorType {
return CompressorLz4
}

+ 0
- 40
pkg/compressor/lz4_compress_test.go View File

@@ -1,40 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLz4Compress(t *testing.T) {
sample := strings.Repeat("hello world", 100)

lz4 := Lz4{}

compressResult, err := lz4.Compress([]byte(sample))
assert.NoError(t, err)
t.Logf("Compressed result: %v", string(compressResult))

decompressResult, err := lz4.Decompress(compressResult)
assert.NoError(t, err)
assert.Equal(t, sample, string(decompressResult))
t.Logf("Decompressed result: %v", string(decompressResult))
}

+ 0
- 33
pkg/compressor/none_compressor.go View File

@@ -1,33 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

type NoneCompressor struct {
}

func (n *NoneCompressor) Compress(data []byte) ([]byte, error) {
return data, nil
}

func (n *NoneCompressor) Decompress(data []byte) ([]byte, error) {
return data, nil
}

func (n *NoneCompressor) GetCompressorType() CompressorType {
return CompressorNone
}

+ 0
- 59
pkg/compressor/zip_compress.go View File

@@ -1,59 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"bytes"
"io"

"github.com/klauspost/compress/zlib"
)

type Zip struct{}

func (z Zip) Compress(data []byte) ([]byte, error) {
var buf bytes.Buffer
var zp = zlib.NewWriter(&buf)
if _, err := zp.Write(data); err != nil {
return nil, err
}
if err := zp.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func (z Zip) Decompress(data []byte) ([]byte, error) {
var buf bytes.Buffer
buf.Write(data)
r, err := zlib.NewReader(&buf)
if err != nil {
return nil, err
}
if err := r.Close(); err != nil {
return nil, err
}
if _, err := io.Copy(&buf, r); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func (z Zip) GetCompressorType() CompressorType {
return CompressorZstd
}

+ 0
- 40
pkg/compressor/zip_compress_test.go View File

@@ -1,40 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestZipCompress(t *testing.T) {
str := "test"

g := &Zip{}

compressRes, err := g.Compress([]byte(str))
assert.NoError(t, err)
t.Logf("compress res: %v", string(compressRes))

decompressRes, err := g.Decompress(compressRes)
assert.NoError(t, err)
t.Logf("decompress res: %v", string(decompressRes))

assert.Equal(t, str, string(decompressRes))
}

+ 0
- 44
pkg/compressor/zstd_compress.go View File

@@ -1,44 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"github.com/klauspost/compress/zstd"
)

type Zstd struct{}

func (z Zstd) Compress(data []byte) ([]byte, error) {
var encoder, err = zstd.NewWriter(nil)
if err != nil {
return nil, err
}
return encoder.EncodeAll(data, make([]byte, 0, len(data))), nil
}

func (z Zstd) Decompress(data []byte) ([]byte, error) {
var decoder, err = zstd.NewReader(nil)
if err != nil {
return nil, err
}
return decoder.DecodeAll(data, nil)
}

func (z Zstd) GetCompressorType() CompressorType {
return CompressorZstd
}

+ 0
- 48
pkg/compressor/zstd_compress_test.go View File

@@ -1,48 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package compressor

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestZstdCompress(t *testing.T) {
ts := []struct {
text string
}{
{
text: strings.Repeat("Don't communicate by sharing memory, share memory by communicating.", 1000),
},
{
text: "88888msj0*&^^%$$#$@!~jjdjfjdlfjkhhdh//><|}{{|\"",
},
}

dc := &Zstd{}
assert.EqualValues(t, CompressorZstd, dc.GetCompressorType())

for _, s := range ts {
var data = []byte(s.text)
dataCompressed, _ := dc.Compress(data)
ret, _ := dc.Decompress(dataCompressed)
assert.EqualValues(t, s.text, string(ret))
}
}

+ 0
- 42
pkg/constant/context.go View File

@@ -1,42 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package constant

const (
ActionStartTime = "action-start-time"
HostName = "host-name"
ActionContext = "actionContext"

PrepareMethod = "sys::prepare"
CommitMethod = "sys::commit"
RollbackMethod = "sys::rollback"
ActionName = "actionName"

SeataXidKey = "SEATA_XID"
XidKey = "TX_XID"
XidKeyLowercase = "tx_xid"
MdcXidKey = "X-TX-XID"
MdcBranchIDKey = "X-TX-BRANCH-ID"
BranchTypeKey = "TX_BRANCH_TYPE"
GlobalLockKey = "TX_LOCK"
SeataFilterKey = "seataDubboFilter"

SeataVersion = "1.1.0"

TccBusinessActionContextParameter = "tccParam"
)

+ 0
- 26
pkg/datasource/init.go View File

@@ -1,26 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package datasource

import (
"seata.apache.org/seata-go/pkg/datasource/sql"
)

func Init() {
sql.Init()
}

+ 0
- 213
pkg/datasource/sql/async_worker.go View File

@@ -1,213 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"flag"
"time"

"seata.apache.org/seata-go/pkg/rm"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"

"seata.apache.org/seata-go/pkg/datasource/sql/datasource"
"seata.apache.org/seata-go/pkg/datasource/sql/undo"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/util/fanout"
"seata.apache.org/seata-go/pkg/util/log"
)

type phaseTwoContext struct {
Xid string
BranchID int64
ResourceID string
}

type AsyncWorkerConfig struct {
BufferLimit int `yaml:"buffer_limit" json:"buffer_limit"`
BufferCleanInterval time.Duration `yaml:"buffer_clean_interval" json:"buffer_clean_interval"`
ReceiveChanSize int `yaml:"receive_chan_size" json:"receive_chan_size"`
CommitWorkerCount int `yaml:"commit_worker_count" json:"commit_worker_count"`
CommitWorkerBufferSize int `yaml:"commit_worker_buffer_size" json:"commit_worker_buffer_size"`
}

func (cfg *AsyncWorkerConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
f.IntVar(&cfg.BufferLimit, prefix+".buffer_size", 10000, "async worker commit buffer limit.")
f.DurationVar(&cfg.BufferCleanInterval, prefix+".buffer.clean_interval", time.Second, "async worker commit buffer interval")
f.IntVar(&cfg.ReceiveChanSize, prefix+".channel_size", 10000, "async worker commit channel size")
f.IntVar(&cfg.CommitWorkerCount, prefix+".worker_count", 10, "async worker commit worker count")
f.IntVar(&cfg.CommitWorkerBufferSize, prefix+".worker_buffer_size", 1000, "async worker commit worker buffer size")
}

// AsyncWorker executor for branch transaction commit and undo log
type AsyncWorker struct {
conf AsyncWorkerConfig

commitQueue chan phaseTwoContext
resourceMgr datasource.DataSourceManager
commitWorker *fanout.Fanout

branchCommitTotal prometheus.Counter
doBranchCommitFailureTotal prometheus.Counter
receiveChanLength prometheus.Gauge
rePutBackToQueue prometheus.Counter
}

func NewAsyncWorker(prom prometheus.Registerer, conf AsyncWorkerConfig, sourceManager datasource.DataSourceManager) *AsyncWorker {
var asyncWorker AsyncWorker
asyncWorker.conf = conf
asyncWorker.commitQueue = make(chan phaseTwoContext, asyncWorker.conf.ReceiveChanSize)
asyncWorker.resourceMgr = sourceManager
asyncWorker.commitWorker = fanout.New("asyncWorker",
fanout.WithWorker(asyncWorker.conf.CommitWorkerCount),
fanout.WithBuffer(asyncWorker.conf.CommitWorkerBufferSize),
)

asyncWorker.branchCommitTotal = promauto.With(prom).NewCounter(prometheus.CounterOpts{
Name: "async_worker_branch_commit_total",
Help: "the total count of branch commit total count",
})
asyncWorker.doBranchCommitFailureTotal = promauto.With(prom).NewCounter(prometheus.CounterOpts{
Name: "async_worker_branch_commit_failure_total",
Help: "the total count of branch commit failure count",
})
asyncWorker.receiveChanLength = promauto.With(prom).NewGauge(prometheus.GaugeOpts{
Name: "async_worker_receive_channel_length",
Help: "the current length of the receive channel size",
})
asyncWorker.rePutBackToQueue = promauto.With(prom).NewCounter(prometheus.CounterOpts{
Name: "async_worker_commit_failure_retry_counter",
Help: "the counter of commit failure retry counter",
})

go asyncWorker.run()

return &asyncWorker
}

// BranchCommit commit branch transaction
func (aw *AsyncWorker) BranchCommit(ctx context.Context, req rm.BranchResource) (branch.BranchStatus, error) {
phaseCtx := phaseTwoContext{
Xid: req.Xid,
BranchID: req.BranchId,
ResourceID: req.ResourceId,
}

aw.branchCommitTotal.Add(1)

select {
case aw.commitQueue <- phaseCtx:
case <-ctx.Done():
}

aw.receiveChanLength.Add(float64(len(aw.commitQueue)))

return branch.BranchStatusPhasetwoCommitted, nil
}

func (aw *AsyncWorker) run() {
ticker := time.NewTicker(aw.conf.BufferCleanInterval)
phaseCtxs := make([]phaseTwoContext, 0, aw.conf.BufferLimit)
for {
select {
case phaseCtx := <-aw.commitQueue:
phaseCtxs = append(phaseCtxs, phaseCtx)
if len(phaseCtxs) >= aw.conf.BufferLimit*2/3 {
aw.doBranchCommit(&phaseCtxs)
}
case <-ticker.C:
aw.doBranchCommit(&phaseCtxs)
}
}
}

func (aw *AsyncWorker) doBranchCommit(phaseCtxs *[]phaseTwoContext) {
if len(*phaseCtxs) == 0 {
return
}

copyPhaseCtxs := make([]phaseTwoContext, len(*phaseCtxs))
copy(copyPhaseCtxs, *phaseCtxs)
*phaseCtxs = (*phaseCtxs)[:0]

doBranchCommit := func(ctx context.Context) {
groupCtxs := make(map[string][]phaseTwoContext, 16)
for i := range copyPhaseCtxs {
if copyPhaseCtxs[i].ResourceID == "" {
continue
}

if _, ok := groupCtxs[copyPhaseCtxs[i].ResourceID]; !ok {
groupCtxs[copyPhaseCtxs[i].ResourceID] = make([]phaseTwoContext, 0, 4)
}

ctxs := groupCtxs[copyPhaseCtxs[i].ResourceID]
ctxs = append(ctxs, copyPhaseCtxs[i])
groupCtxs[copyPhaseCtxs[i].ResourceID] = ctxs
}

for k := range groupCtxs {
aw.dealWithGroupedContexts(k, groupCtxs[k])
}
}

if err := aw.commitWorker.Do(context.Background(), doBranchCommit); err != nil {
aw.doBranchCommitFailureTotal.Add(1)
log.Errorf("do branch commit err:%v,phaseCtxs=%v", err, phaseCtxs)
}
}

func (aw *AsyncWorker) dealWithGroupedContexts(resID string, phaseCtxs []phaseTwoContext) {
val, ok := aw.resourceMgr.GetCachedResources().Load(resID)
if !ok {
for i := range phaseCtxs {
aw.rePutBackToQueue.Add(1)
aw.commitQueue <- phaseCtxs[i]
}
return
}

res := val.(*DBResource)
conn, err := res.db.Conn(context.Background())
if err != nil {
for i := range phaseCtxs {
aw.commitQueue <- phaseCtxs[i]
}
}

defer conn.Close()

undoMgr, err := undo.GetUndoLogManager(res.dbType)
if err != nil {
for i := range phaseCtxs {
aw.rePutBackToQueue.Add(1)
aw.commitQueue <- phaseCtxs[i]
}
return
}

for i := range phaseCtxs {
phaseCtx := phaseCtxs[i]
if err := undoMgr.BatchDeleteUndoLog([]string{phaseCtx.Xid}, []int64{phaseCtx.BranchID}, conn); err != nil {
aw.rePutBackToQueue.Add(1)
aw.commitQueue <- phaseCtx
}
}
}

+ 0
- 129
pkg/datasource/sql/at_resource_manager.go View File

@@ -1,129 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"database/sql"
"fmt"
"sync"

"github.com/prometheus/client_golang/prometheus"

"seata.apache.org/seata-go/pkg/datasource/sql/datasource"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/datasource/sql/undo"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/rm"
serr "seata.apache.org/seata-go/pkg/util/errors"
)

func InitAT(cfg undo.Config, asyncCfg AsyncWorkerConfig) {
atSourceManager := &ATSourceManager{
resourceCache: sync.Map{},
basic: datasource.NewBasicSourceManager(),
rmRemoting: rm.GetRMRemotingInstance(),
}

undo.InitUndoConfig(cfg)
atSourceManager.worker = NewAsyncWorker(prometheus.DefaultRegisterer, asyncCfg, atSourceManager)
rm.GetRmCacheInstance().RegisterResourceManager(atSourceManager)
}

type ATSourceManager struct {
resourceCache sync.Map
worker *AsyncWorker
basic *datasource.BasicSourceManager
rmRemoting *rm.RMRemoting
}

func (a *ATSourceManager) GetBranchType() branch.BranchType {
return branch.BranchTypeAT
}

// GetCachedResources get all resources managed by this manager
func (a *ATSourceManager) GetCachedResources() *sync.Map {
return &a.resourceCache
}

// RegisterResource register a Resource to be managed by Resource Manager
func (a *ATSourceManager) RegisterResource(res rm.Resource) error {
a.resourceCache.Store(res.GetResourceId(), res)
return a.basic.RegisterResource(res)
}

// UnregisterResource unregister a Resource from the Resource Manager
func (a *ATSourceManager) UnregisterResource(res rm.Resource) error {
return a.basic.UnregisterResource(res)
}

// BranchRollback rollback a branch transaction
func (a *ATSourceManager) BranchRollback(ctx context.Context, branchResource rm.BranchResource) (branch.BranchStatus, error) {
var dbResource *DBResource
if resource, ok := a.resourceCache.Load(branchResource.ResourceId); !ok {
err := fmt.Errorf("DB resource is not exist, resourceId: %s", branchResource.ResourceId)
return branch.BranchStatusUnknown, err
} else {
dbResource, _ = resource.(*DBResource)
}

undoMgr, err := undo.GetUndoLogManager(dbResource.dbType)
if err != nil {
return branch.BranchStatusUnknown, err
}

if err := undoMgr.RunUndo(ctx, branchResource.Xid, branchResource.BranchId, dbResource.db, dbResource.dbName); err != nil {
transErr, ok := err.(*serr.SeataError)
if !ok {
return branch.BranchStatusPhaseoneFailed, err
}

if transErr.Code == serr.TransactionErrorCodeBranchRollbackFailedUnretriable {
return branch.BranchStatusPhasetwoRollbackFailedUnretryable, nil
}

return branch.BranchStatusPhasetwoRollbackFailedRetryable, nil
}

return branch.BranchStatusPhasetwoRollbacked, nil
}

// BranchCommit commit the branch transaction
func (a *ATSourceManager) BranchCommit(ctx context.Context, resource rm.BranchResource) (branch.BranchStatus, error) {
a.worker.BranchCommit(ctx, resource)
return branch.BranchStatusPhasetwoCommitted, nil
}

func (a *ATSourceManager) LockQuery(ctx context.Context, param rm.LockQueryParam) (bool, error) {
return a.rmRemoting.LockQuery(param)
}

// BranchRegister branch transaction register
func (a *ATSourceManager) BranchRegister(ctx context.Context, req rm.BranchRegisterParam) (int64, error) {
return a.rmRemoting.BranchRegister(req)
}

// BranchReport Report status of transaction branch
func (a *ATSourceManager) BranchReport(ctx context.Context, param rm.BranchReportParam) error {
return a.rmRemoting.BranchReport(param)
}

func (a *ATSourceManager) CreateTableMetaCache(ctx context.Context, resID string, dbType types.DBType,
db *sql.DB) (datasource.TableMetaCache, error) {
return a.basic.CreateTableMetaCache(ctx, resID, dbType, db)
}

+ 0
- 272
pkg/datasource/sql/conn.go View File

@@ -1,272 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"database/sql/driver"

"seata.apache.org/seata-go/pkg/datasource/sql/exec"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/datasource/sql/util"
)

// Conn is a connection to a database. It is not used concurrently
// by multiple goroutines.
//
// Conn is assumed to be stateful.
type Conn struct {
res *DBResource
txCtx *types.TransactionContext
targetConn driver.Conn
autoCommit bool
dbName string
dbType types.DBType
}

// ResetSession is called prior to executing a query on the connection
// if the connection has been used before. If the driver returns ErrBadConn
// the connection is discarded.
func (c *Conn) ResetSession(ctx context.Context) error {
conn, ok := c.targetConn.(driver.SessionResetter)
if !ok {
return driver.ErrSkip
}

c.autoCommit = true
c.txCtx = types.NewTxCtx()
return conn.ResetSession(ctx)
}

// Prepare returns a prepared statement, bound to this connection.
func (c *Conn) Prepare(query string) (driver.Stmt, error) {
s, err := c.targetConn.Prepare(query)
if err != nil {
return nil, err
}

return &Stmt{
conn: c,
stmt: s,
query: query,
res: c.res,
txCtx: c.txCtx,
}, nil
}

// PrepareContext
func (c *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
conn, ok := c.targetConn.(driver.ConnPrepareContext)
if !ok {
stmt, err := c.targetConn.Prepare(query)
if err != nil {
return nil, err
}

return &Stmt{stmt: stmt, query: query, res: c.res, txCtx: c.txCtx}, nil
}

s, err := conn.PrepareContext(ctx, query)
if err != nil {
return nil, err
}

return &Stmt{
conn: c,
stmt: s,
query: query,
res: c.res,
txCtx: c.txCtx,
}, nil
}

// Exec warning: if you want to use global transaction, please use ExecContext function
func (c *Conn) Exec(query string, args []driver.Value) (driver.Result, error) {
conn, ok := c.targetConn.(driver.Execer)
if !ok {
return nil, driver.ErrSkip
}

ret, err := conn.Exec(query, args)
if err != nil {
return nil, err
}

return types.NewResult(types.WithResult(ret)).GetResult(), nil
}

// ExecContext
func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
targetConn, ok := c.targetConn.(driver.ExecerContext)
if !ok {
values := make([]driver.Value, 0, len(args))
for i := range args {
values = append(values, args[i].Value)
}
return c.Exec(query, values)
}

ret, err := targetConn.ExecContext(ctx, query, args)
if err != nil {
return nil, err
}
return types.NewResult(types.WithResult(ret)).GetResult(), nil
}

// Query
func (c *Conn) Query(query string, args []driver.Value) (driver.Rows, error) {
conn, ok := c.targetConn.(driver.Queryer)
if !ok {
return nil, driver.ErrSkip
}

executor, err := exec.BuildExecutor(c.res.dbType, c.txCtx.TransactionMode, query)
if err != nil {
return nil, err
}

execCtx := &types.ExecContext{
TxCtx: c.txCtx,
Query: query,
Values: args,
}

ret, err := executor.ExecWithValue(context.Background(), execCtx,
func(ctx context.Context, query string, args []driver.NamedValue) (types.ExecResult, error) {
ret, err := conn.Query(query, util.NamedValueToValue(args))
if err != nil {
return nil, err
}

return types.NewResult(types.WithRows(ret)), nil
})
if err != nil {
return nil, err
}

return ret.GetRows(), nil
}

// QueryContext
func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
conn, ok := c.targetConn.(driver.QueryerContext)
if !ok {
values := make([]driver.Value, 0, len(args))

for i := range args {
values = append(values, args[i].Value)
}

return c.Query(query, values)
}

ret, err := conn.QueryContext(ctx, query, args)
if err != nil {
return nil, err
}

return types.NewResult(types.WithRows(ret)).GetRows(), nil
}

// Begin starts and returns a new transaction.
//
// Deprecated: Drivers should implement ConnBeginTx instead (or additionally).
func (c *Conn) Begin() (driver.Tx, error) {
c.autoCommit = false

tx, err := c.targetConn.Begin()
if err != nil {
return nil, err
}

if c.txCtx == nil {
c.txCtx = types.NewTxCtx()
c.txCtx.DBType = c.res.dbType
c.txCtx.TxOpt = driver.TxOptions{}
}

return newTx(
withDriverConn(c),
withTxCtx(c.txCtx),
withOriginTx(tx),
)
}

// BeginTx Open a transaction and judge whether the current transaction needs to open a
//
// global transaction according to tranCtx. If so, it needs to be included in the transaction management of seata
func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if c.txCtx.TransactionMode == types.XAMode {
c.autoCommit = false
return newTx(
withDriverConn(c),
withTxCtx(c.txCtx),
withOriginTx(nil),
)
}

if conn, ok := c.targetConn.(driver.ConnBeginTx); ok {
tx, err := conn.BeginTx(ctx, opts)
if err != nil {
return nil, err
}

return newTx(
withDriverConn(c),
withTxCtx(c.txCtx),
withOriginTx(tx),
)
}

txi, err := c.Begin()
if err != nil {
return nil, err
}
return newTx(
withDriverConn(c),
withTxCtx(c.txCtx),
withOriginTx(txi),
)
}

func (c *Conn) GetAutoCommit() bool {
return c.autoCommit
}

func (c *Conn) GetDbVersion() string {
if c.res == nil {
return ""
}
return c.res.GetDbVersion()
}

// Close invalidates and potentially stops any current
// prepared statements and transactions, marking this
// connection as no longer in use.
//
// Because the sql package maintains a free pool of
// connections and only calls Close when there's a surplus of
// idle connections, it shouldn't be necessary for drivers to
// do their own connection caching.
//
// Drivers must ensure all network calls made by Close
// do not block indefinitely (e.g. apply a timeout).
func (c *Conn) Close() error {
c.txCtx = types.NewTxCtx()
return c.targetConn.Close()
}

+ 0
- 162
pkg/datasource/sql/conn_at.go View File

@@ -1,162 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
gosql "database/sql"
"database/sql/driver"

"seata.apache.org/seata-go/pkg/datasource/sql/exec"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/tm"
"seata.apache.org/seata-go/pkg/util/log"
)

// ATConn Database connection proxy object under XA transaction model
// Conn is assumed to be stateful.
type ATConn struct {
*Conn
}

func (c *ATConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if c.createOnceTxContext(ctx) {
defer func() {
c.txCtx = types.NewTxCtx()
}()
}
return c.Conn.PrepareContext(ctx, query)
}

// ExecContext
func (c *ATConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
if c.createOnceTxContext(ctx) {
defer func() {
c.txCtx = types.NewTxCtx()
}()
}

ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types.ExecResult, error) {
executor, err := exec.BuildExecutor(c.res.dbType, c.txCtx.TransactionMode, query)
if err != nil {
return nil, err
}

execCtx := &types.ExecContext{
TxCtx: c.txCtx,
Query: query,
NamedValues: args,
Conn: c.targetConn,
DBName: c.dbName,
DbVersion: c.GetDbVersion(),
IsSupportsSavepoints: true,
IsAutoCommit: c.GetAutoCommit(),
}

ret, err := executor.ExecWithNamedValue(ctx, execCtx,
func(ctx context.Context, query string, args []driver.NamedValue) (types.ExecResult, error) {
ret, err := c.Conn.ExecContext(ctx, query, args)
if err != nil {
return nil, err
}
return types.NewResult(types.WithResult(ret)), nil
})

return ret, err
})
if err != nil {
return nil, err
}
return ret.GetResult(), nil
}

// BeginTx
func (c *ATConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
c.autoCommit = false

c.txCtx = types.NewTxCtx()
c.txCtx.DBType = c.res.dbType
c.txCtx.TxOpt = opts
c.txCtx.ResourceID = c.res.resourceID

if tm.IsGlobalTx(ctx) {
c.txCtx.XID = tm.GetXID(ctx)
c.txCtx.TransactionMode = types.ATMode
}

tx, err := c.Conn.BeginTx(ctx, opts)
if err != nil {
return nil, err
}

return &ATTx{tx: tx.(*Tx)}, nil
}

func (c *ATConn) createOnceTxContext(ctx context.Context) bool {
onceTx := tm.IsGlobalTx(ctx) && c.autoCommit

if onceTx {
c.txCtx = types.NewTxCtx()
c.txCtx.DBType = c.res.dbType
c.txCtx.ResourceID = c.res.resourceID
c.txCtx.XID = tm.GetXID(ctx)
c.txCtx.TransactionMode = types.ATMode
c.txCtx.GlobalLockRequire = true
}

return onceTx
}

func (c *ATConn) createNewTxOnExecIfNeed(ctx context.Context, f func() (types.ExecResult, error)) (types.ExecResult, error) {
var (
tx driver.Tx
err error
)

if c.txCtx.TransactionMode != types.Local && tm.IsGlobalTx(ctx) && c.autoCommit {
tx, err = c.BeginTx(ctx, driver.TxOptions{Isolation: driver.IsolationLevel(gosql.LevelDefault)})
if err != nil {
return nil, err
}
}
defer func() {
recoverErr := recover()
if recoverErr != nil {
log.Errorf("at exec panic, recoverErr:%v", recoverErr)
if tx != nil {
rollbackErr := tx.Rollback()
if rollbackErr != nil {
log.Errorf("conn at rollback error:%v", rollbackErr)
}
}
}
}()

ret, err := f()
if err != nil {
return nil, err
}

if tx != nil {
if err := tx.Commit(); err != nil {
return nil, err
}
}

return ret, nil
}

+ 0
- 240
pkg/datasource/sql/conn_at_test.go View File

@@ -1,240 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"database/sql"
"database/sql/driver"
"sync/atomic"
"testing"

"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"

"seata.apache.org/seata-go/pkg/datasource/sql/exec"
"seata.apache.org/seata-go/pkg/datasource/sql/mock"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/tm"
)

func TestMain(m *testing.M) {
Init()
m.Run()
}

func initAtConnTestResource(t *testing.T) (*gomock.Controller, *sql.DB, *mockSQLInterceptor, *mockTxHook) {
ctrl := gomock.NewController(t)

mockMgr := initMockResourceManager(branch.BranchTypeAT, ctrl)
_ = mockMgr

db, err := sql.Open(SeataATMySQLDriver, "root:12345678@tcp(127.0.0.1:3306)/seata_client?multiStatements=true")
if err != nil {
t.Fatal(err)
}

_ = initMockAtConnector(t, ctrl, db, func(t *testing.T, ctrl *gomock.Controller) driver.Connector {
mockTx := mock.NewMockTestDriverTx(ctrl)
mockTx.EXPECT().Commit().AnyTimes().Return(nil)
mockTx.EXPECT().Rollback().AnyTimes().Return(nil)

mockConn := mock.NewMockTestDriverConn(ctrl)
mockConn.EXPECT().Begin().AnyTimes().Return(mockTx, nil)
mockConn.EXPECT().BeginTx(gomock.Any(), gomock.Any()).AnyTimes().Return(mockTx, nil)
mockConn.EXPECT().QueryContext(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
rows := &mysqlMockRows{}
rows.data = [][]interface{}{
{"8.0.29"},
}
return rows, nil
})
baseMockConn(mockConn)

connector := mock.NewMockTestDriverConnector(ctrl)
connector.EXPECT().Connect(gomock.Any()).AnyTimes().Return(mockConn, nil)
return connector
})

mi := &mockSQLInterceptor{}

ti := &mockTxHook{}

exec.CleanCommonHook()
CleanTxHooks()
exec.RegisterCommonHook(mi)
RegisterTxHook(ti)

return ctrl, db, mi, ti
}

func TestATConn_ExecContext(t *testing.T) {
ctrl, db, mi, ti := initAtConnTestResource(t)
defer func() {
ctrl.Finish()
db.Close()
CleanTxHooks()
}()

t.Run("have xid", func(t *testing.T) {
ctx := tm.InitSeataContext(context.Background())
tm.SetXID(ctx, uuid.New().String())
t.Logf("set xid=%s", tm.GetXID(ctx))

beforeHook := func(_ context.Context, execCtx *types.ExecContext) {
t.Logf("on exec xid=%s", execCtx.TxCtx.XID)
assert.Equal(t, tm.GetXID(ctx), execCtx.TxCtx.XID)
assert.Equal(t, types.ATMode, execCtx.TxCtx.TransactionMode)
}
mi.before = beforeHook

var comitCnt int32
beforeCommit := func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
assert.Equal(t, types.ATMode, tx.tranCtx.TransactionMode)
}
ti.beforeCommit = beforeCommit

conn, err := db.Conn(context.Background())
assert.NoError(t, err)

_, err = conn.ExecContext(ctx, "SELECT 1")
assert.NoError(t, err)
_, err = db.ExecContext(ctx, "SELECT 1")
assert.NoError(t, err)

assert.Equal(t, int32(2), atomic.LoadInt32(&comitCnt))
})

t.Run("not xid", func(t *testing.T) {
mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, "", execCtx.TxCtx.XID)
assert.Equal(t, types.Local, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

conn, err := db.Conn(context.Background())
assert.NoError(t, err)

_, err = conn.ExecContext(context.Background(), "SELECT 1")
assert.NoError(t, err)
_, err = db.ExecContext(context.Background(), "SELECT 1")
assert.NoError(t, err)

_, err = db.Exec("SELECT 1")
assert.NoError(t, err)

assert.Equal(t, int32(0), atomic.LoadInt32(&comitCnt))
})
}

func TestATConn_BeginTx(t *testing.T) {
ctrl, db, mi, ti := initAtConnTestResource(t)
defer func() {
ctrl.Finish()
db.Close()
CleanTxHooks()
}()

t.Run("tx-local", func(t *testing.T) {
tx, err := db.Begin()
assert.NoError(t, err)

mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, "", execCtx.TxCtx.XID)
assert.Equal(t, types.Local, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

_, err = tx.ExecContext(tm.InitSeataContext(context.Background()), "SELECT * FROM user")
assert.NoError(t, err)

err = tx.Commit()
assert.NoError(t, err)

assert.Equal(t, int32(1), atomic.LoadInt32(&comitCnt))
})

t.Run("tx-local-context", func(t *testing.T) {
tx, err := db.BeginTx(context.Background(), &sql.TxOptions{})
assert.NoError(t, err)

mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, "", execCtx.TxCtx.XID)
assert.Equal(t, types.Local, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

_, err = tx.ExecContext(tm.InitSeataContext(context.Background()), "SELECT * FROM user")
assert.NoError(t, err)

err = tx.Commit()
assert.NoError(t, err)

assert.Equal(t, int32(1), atomic.LoadInt32(&comitCnt))
})

t.Run("tx-at-context", func(t *testing.T) {
ctx := tm.InitSeataContext(context.Background())
tm.SetXID(ctx, uuid.NewString())
tx, err := db.BeginTx(ctx, &sql.TxOptions{})
assert.NoError(t, err)

mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, tm.GetXID(ctx), execCtx.TxCtx.XID)
assert.Equal(t, types.ATMode, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

err = tx.Commit()
assert.NoError(t, err)

assert.Equal(t, int32(1), atomic.LoadInt32(&comitCnt))
})
}

+ 0
- 406
pkg/datasource/sql/conn_xa.go View File

@@ -1,406 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
gosql "database/sql"
"database/sql/driver"
"errors"
"fmt"
"time"

"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/datasource/sql/xa"
"seata.apache.org/seata-go/pkg/tm"
"seata.apache.org/seata-go/pkg/util/log"
)

var xaConnTimeout time.Duration

// XAConn Database connection proxy object under XA transaction model
// Conn is assumed to be stateful.
type XAConn struct {
*Conn

tx driver.Tx
xaResource xa.XAResource
xaBranchXid *XABranchXid
xaActive bool
rollBacked bool
branchRegisterTime time.Time
prepareTime time.Time
isConnKept bool
}

func (c *XAConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if c.createOnceTxContext(ctx) {
defer func() {
c.txCtx = types.NewTxCtx()
}()
}

//ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types, error) {
// ret, err := c.Conn.PrepareContext(ctx, query)
// if err != nil {
// return nil, err
// }
// return types.NewResult(types.WithRows(ret)), nil
//})

return c.Conn.PrepareContext(ctx, query)
}

// QueryContext exec xa sql
func (c *XAConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
if c.createOnceTxContext(ctx) {
defer func() {
c.txCtx = types.NewTxCtx()
}()
}

ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types.ExecResult, error) {
ret, err := c.Conn.QueryContext(ctx, query, args)
if err != nil {
return nil, err
}
return types.NewResult(types.WithRows(ret)), nil
})
if err != nil {
return nil, err
}
return ret.GetRows(), nil
}

func (c *XAConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
if c.createOnceTxContext(ctx) {
defer func() {
c.txCtx = types.NewTxCtx()
}()
}

ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types.ExecResult, error) {
ret, err := c.Conn.ExecContext(ctx, query, args)
if err != nil {
return nil, err
}
return types.NewResult(types.WithResult(ret)), nil
})

if err != nil {
return nil, err
}

return ret.GetResult(), nil
}

// BeginTx like common transaction. but it just exec XA START
func (c *XAConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if !tm.IsGlobalTx(ctx) {
tx, err := c.Conn.BeginTx(ctx, opts)
return tx, err
}

c.autoCommit = false

c.txCtx = types.NewTxCtx()
c.txCtx.DBType = c.res.dbType
c.txCtx.TxOpt = opts
c.txCtx.ResourceID = c.res.resourceID
c.txCtx.XID = tm.GetXID(ctx)
c.txCtx.TransactionMode = types.XAMode

tx, err := c.Conn.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
c.tx = tx

if !c.autoCommit {
if c.xaActive {
return nil, errors.New("should NEVER happen: setAutoCommit from true to false while xa branch is active")
}

baseTx, ok := tx.(*Tx)
if !ok {
return nil, fmt.Errorf("start xa %s transaction failure for the tx is a wrong type", c.txCtx.XID)
}

c.branchRegisterTime = time.Now()
if err := baseTx.register(c.txCtx); err != nil {
c.cleanXABranchContext()
return nil, fmt.Errorf("failed to register xa branch %s, err:%w", c.txCtx.XID, err)
}

c.xaBranchXid = XaIdBuild(c.txCtx.XID, c.txCtx.BranchID)
c.keepIfNecessary()

if err = c.start(ctx); err != nil {
c.cleanXABranchContext()
return nil, fmt.Errorf("failed to start xa branch xid:%s err:%w", c.txCtx.XID, err)
}
c.xaActive = true
}

return &XATx{tx: tx.(*Tx)}, nil
}

func (c *XAConn) createOnceTxContext(ctx context.Context) bool {
onceTx := tm.IsGlobalTx(ctx) && c.autoCommit

if onceTx {
c.txCtx = types.NewTxCtx()
c.txCtx.DBType = c.res.dbType
c.txCtx.ResourceID = c.res.resourceID
c.txCtx.XID = tm.GetXID(ctx)
c.txCtx.TransactionMode = types.XAMode
c.txCtx.GlobalLockRequire = true
}

return onceTx
}

func (c *XAConn) createNewTxOnExecIfNeed(ctx context.Context, f func() (types.ExecResult, error)) (types.ExecResult, error) {
var (
tx driver.Tx
err error
)

defer func() {
recoverErr := recover()
if err != nil || recoverErr != nil {
log.Errorf("conn at rollback error:%v or recoverErr:%v", err, recoverErr)
if c.tx != nil {
rollbackErr := c.tx.Rollback()
if rollbackErr != nil {
log.Errorf("conn at rollback error:%v", rollbackErr)
}
}
}
}()

currentAutoCommit := c.autoCommit
if c.txCtx.TransactionMode != types.Local && tm.IsGlobalTx(ctx) && c.autoCommit {
tx, err = c.BeginTx(ctx, driver.TxOptions{Isolation: driver.IsolationLevel(gosql.LevelDefault)})
if err != nil {
return nil, err
}
}

// execute SQL
ret, err := f()
if err != nil {
// XA End & Rollback
if rollbackErr := c.Rollback(ctx); rollbackErr != nil {
log.Errorf("failed to rollback xa branch of :%s, err:%w", c.txCtx.XID, rollbackErr)
}
return nil, err
}

if tx != nil && currentAutoCommit {
if err = c.Commit(ctx); err != nil {
log.Errorf("xa connection proxy commit failure xid:%s, err:%v", c.txCtx.XID, err)
// XA End & Rollback
if err := c.Rollback(ctx); err != nil {
log.Errorf("xa connection proxy rollback failure xid:%s, err:%v", c.txCtx.XID, err)
}
}
}

return ret, nil
}

func (c *XAConn) keepIfNecessary() {
if c.ShouldBeHeld() {
if err := c.res.Hold(c.xaBranchXid.String(), c); err == nil {
c.isConnKept = true
}
}
}

func (c *XAConn) releaseIfNecessary() {
if c.ShouldBeHeld() && c.xaBranchXid.String() != "" {
if c.isConnKept {
c.res.Release(c.xaBranchXid.String())
c.isConnKept = false
}
}
}

func (c *XAConn) start(ctx context.Context) error {
xaResource, err := xa.CreateXAResource(c.Conn.targetConn, c.dbType)
if err != nil {
return fmt.Errorf("create xa xid:%s resoruce err:%w", c.txCtx.XID, err)
}
c.xaResource = xaResource

if err := c.xaResource.Start(ctx, c.xaBranchXid.String(), xa.TMNoFlags); err != nil {
return fmt.Errorf("xa xid %s resource connection start err:%w", c.txCtx.XID, err)
}

if err := c.termination(c.xaBranchXid.String()); err != nil {
c.xaResource.End(ctx, c.xaBranchXid.String(), xa.TMFail)
c.XaRollback(ctx, c.xaBranchXid)
return err
}
return err
}

func (c *XAConn) end(ctx context.Context, flags int) error {
err := c.xaResource.End(ctx, c.xaBranchXid.String(), flags)
if err != nil {
return err
}
err = c.termination(c.xaBranchXid.String())
if err != nil {
return err
}
return nil
}

func (c *XAConn) termination(xaBranchXid string) error {
branchStatus, err := branchStatus(xaBranchXid)
if err != nil {
c.releaseIfNecessary()
return fmt.Errorf("failed xa branch [%v] the global transaction has finish, branch status: [%v]", c.txCtx.XID, branchStatus)
}
return nil
}

func (c *XAConn) cleanXABranchContext() {
h, _ := time.ParseDuration("-1000h")
c.branchRegisterTime = time.Now().Add(h)
c.prepareTime = time.Now().Add(h)
c.xaActive = false
if !c.isConnKept {
c.xaBranchXid = nil
}
}

func (c *XAConn) Rollback(ctx context.Context) error {
if c.autoCommit {
return nil
}

if !c.xaActive || c.xaBranchXid == nil {
return fmt.Errorf("should NOT rollback on an inactive session")
}

if !c.rollBacked {
if c.xaResource.End(ctx, c.xaBranchXid.String(), xa.TMFail) != nil {
return c.rollbackErrorHandle()
}
if c.XaRollback(ctx, c.xaBranchXid) != nil {
c.cleanXABranchContext()
return c.rollbackErrorHandle()
}
if err := c.tx.Rollback(); err != nil {
c.cleanXABranchContext()
return fmt.Errorf("failed to report XA branch commit-failure on xid:%s err:%w", c.txCtx.XID, err)
}
}
c.cleanXABranchContext()
return nil
}

func (c *XAConn) rollbackErrorHandle() error {
return fmt.Errorf("failed to end(TMFAIL) xa branch on [%v] - [%v]", c.txCtx.XID, c.xaBranchXid.GetBranchId())
}

func (c *XAConn) Commit(ctx context.Context) error {
if c.autoCommit {
return nil
}

if !c.xaActive || c.xaBranchXid == nil {
return fmt.Errorf("should NOT commit on an inactive session")
}

now := time.Now()
if c.end(ctx, xa.TMSuccess) != nil {
return c.commitErrorHandle(ctx)
}

if c.checkTimeout(ctx, now) != nil {
return c.commitErrorHandle(ctx)
}

if c.xaResource.XAPrepare(ctx, c.xaBranchXid.String()) != nil {
return c.commitErrorHandle(ctx)
}
return nil
}

func (c *XAConn) commitErrorHandle(ctx context.Context) error {
var err error
if err = c.XaRollback(ctx, c.xaBranchXid); err != nil {
err = fmt.Errorf("failed to report XA branch commit-failure xid:%s, err:%w", c.txCtx.XID, err)
}
c.cleanXABranchContext()
return err
}

func (c *XAConn) ShouldBeHeld() bool {
return c.res.IsShouldBeHeld() || (c.res.GetDbType().String() != "" && c.res.GetDbType() != types.DBTypeUnknown)
}

func (c *XAConn) checkTimeout(ctx context.Context, now time.Time) error {
if now.Sub(c.branchRegisterTime) > xaConnTimeout {
c.XaRollback(ctx, c.xaBranchXid)
return fmt.Errorf("XA branch timeout error xid:%s", c.txCtx.XID)
}
return nil
}

func (c *XAConn) Close() error {
c.rollBacked = false
if c.isConnKept && c.ShouldBeHeld() {
return nil
}
c.cleanXABranchContext()
if err := c.Conn.Close(); err != nil {
return err
}
return nil
}

func (c *XAConn) CloseForce() error {
if err := c.Conn.Close(); err != nil {
return err
}
c.rollBacked = false
c.cleanXABranchContext()
c.releaseIfNecessary()
return nil
}

func (c *XAConn) XaCommit(ctx context.Context, xaXid XAXid) error {
err := c.xaResource.Commit(ctx, xaXid.String(), false)
c.releaseIfNecessary()
return err
}

func (c *XAConn) XaRollbackByBranchId(ctx context.Context, xaXid XAXid) error {
return c.XaRollback(ctx, xaXid)
}

func (c *XAConn) XaRollback(ctx context.Context, xaXid XAXid) error {
err := c.xaResource.Rollback(ctx, xaXid.String())
c.releaseIfNecessary()
return err
}

+ 0
- 331
pkg/datasource/sql/conn_xa_test.go View File

@@ -1,331 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"database/sql"
"database/sql/driver"
"io"
"sync/atomic"
"testing"
"time"

"github.com/bluele/gcache"
"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"

"seata.apache.org/seata-go/pkg/datasource/sql/exec"
"seata.apache.org/seata-go/pkg/datasource/sql/mock"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/tm"
)

type mysqlMockRows struct {
idx int
data [][]interface{}
}

func (m *mysqlMockRows) Columns() []string {
//TODO implement me
panic("implement me")
}

func (m *mysqlMockRows) Close() error {
//TODO implement me
panic("implement me")
}

func (m *mysqlMockRows) Next(dest []driver.Value) error {
if m.idx == len(m.data) {
return io.EOF
}

min := func(a, b int) int {
if a < b {
return a
}
return b
}

cnt := min(len(m.data[0]), len(dest))

for i := 0; i < cnt; i++ {
dest[i] = m.data[m.idx][i]
}
m.idx++
return nil
}

type mockSQLInterceptor struct {
before func(ctx context.Context, execCtx *types.ExecContext)
after func(ctx context.Context, execCtx *types.ExecContext)
}

func (mi *mockSQLInterceptor) Type() types.SQLType {
return types.SQLTypeUnknown
}

// Before
func (mi *mockSQLInterceptor) Before(ctx context.Context, execCtx *types.ExecContext) error {
if mi.before != nil {
mi.before(ctx, execCtx)
}
return nil
}

// After
func (mi *mockSQLInterceptor) After(ctx context.Context, execCtx *types.ExecContext) error {
if mi.after != nil {
mi.after(ctx, execCtx)
}
return nil
}

type mockTxHook struct {
beforeCommit func(tx *Tx)
beforeRollback func(tx *Tx)
}

// BeforeCommit
func (mi *mockTxHook) BeforeCommit(tx *Tx) {
if mi.beforeCommit != nil {
mi.beforeCommit(tx)
}
}

// BeforeRollback
func (mi *mockTxHook) BeforeRollback(tx *Tx) {
if mi.beforeRollback != nil {
mi.beforeRollback(tx)
}
}

func baseMockConn(mockConn *mock.MockTestDriverConn) {
branchStatusCache = gcache.New(1024).LRU().Expiration(time.Minute * 10).Build()

mockConn.EXPECT().ExecContext(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&driver.ResultNoRows, nil)
mockConn.EXPECT().Exec(gomock.Any(), gomock.Any()).AnyTimes().Return(&driver.ResultNoRows, nil)
mockConn.EXPECT().ResetSession(gomock.Any()).AnyTimes().Return(nil)
mockConn.EXPECT().Close().AnyTimes().Return(nil)

mockConn.EXPECT().QueryContext(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
rows := &mysqlMockRows{}
rows.data = [][]interface{}{
{"8.0.29"},
}
return rows, nil
})
}

func initXAConnTestResource(t *testing.T) (*gomock.Controller, *sql.DB, *mockSQLInterceptor, *mockTxHook) {
ctrl := gomock.NewController(t)

mockMgr := initMockResourceManager(branch.BranchTypeXA, ctrl)
_ = mockMgr
//db, err := sql.Open("seata-xa-mysql", "root:seata_go@tcp(127.0.0.1:3306)/seata_go_test?multiStatements=true")
db, err := sql.Open("seata-xa-mysql", "root:12345678@tcp(127.0.0.1:3306)/seata_client?multiStatements=true&interpolateParams=true")
if err != nil {
t.Fatal(err)
}

_ = initMockXaConnector(t, ctrl, db, func(t *testing.T, ctrl *gomock.Controller) driver.Connector {
mockTx := mock.NewMockTestDriverTx(ctrl)
mockTx.EXPECT().Commit().AnyTimes().Return(nil)
mockTx.EXPECT().Rollback().AnyTimes().Return(nil)

mockConn := mock.NewMockTestDriverConn(ctrl)
mockConn.EXPECT().Begin().AnyTimes().Return(mockTx, nil)
mockConn.EXPECT().BeginTx(gomock.Any(), gomock.Any()).AnyTimes().Return(mockTx, nil)
baseMockConn(mockConn)

connector := mock.NewMockTestDriverConnector(ctrl)
connector.EXPECT().Connect(gomock.Any()).AnyTimes().Return(mockConn, nil)
return connector
})

mi := &mockSQLInterceptor{}
ti := &mockTxHook{}

exec.CleanCommonHook()
CleanTxHooks()
exec.RegisterCommonHook(mi)
RegisterTxHook(ti)

return ctrl, db, mi, ti
}

func TestXAConn_ExecContext(t *testing.T) {

ctrl, db, mi, ti := initXAConnTestResource(t)
defer func() {
ctrl.Finish()
db.Close()
CleanTxHooks()
}()

t.Run("have xid", func(t *testing.T) {
ctx := tm.InitSeataContext(context.Background())
tm.SetXID(ctx, uuid.New().String())

before := func(_ context.Context, execCtx *types.ExecContext) {
t.Logf("on exec xid=%s", execCtx.TxCtx.XID)
assert.Equal(t, tm.GetXID(ctx), execCtx.TxCtx.XID)
assert.Equal(t, types.XAMode, execCtx.TxCtx.TransactionMode)
}
mi.before = before

var comitCnt int32
beforeCommit := func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
assert.Equal(t, tx.tranCtx.TransactionMode, types.XAMode)
}
ti.beforeCommit = beforeCommit

conn, err := db.Conn(context.Background())
assert.NoError(t, err)

_, err = conn.ExecContext(ctx, "SELECT 1")
assert.NoError(t, err)
_, err = db.ExecContext(ctx, "SELECT 1")
assert.NoError(t, err)

// todo fix
assert.Equal(t, int32(0), atomic.LoadInt32(&comitCnt))
})

t.Run("not xid", func(t *testing.T) {
before := func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, "", execCtx.TxCtx.XID)
assert.Equal(t, types.Local, execCtx.TxCtx.TransactionMode)
}
mi.before = before

var comitCnt int32
beforeCommit := func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}
ti.beforeCommit = beforeCommit

conn, err := db.Conn(context.Background())
assert.NoError(t, err)

_, err = conn.ExecContext(context.Background(), "SELECT 1")
assert.NoError(t, err)
_, err = db.ExecContext(context.Background(), "SELECT 1")
assert.NoError(t, err)

_, err = db.Exec("SELECT 1")
assert.NoError(t, err)

assert.Equal(t, int32(0), atomic.LoadInt32(&comitCnt))
})
}

func TestXAConn_BeginTx(t *testing.T) {
ctrl, db, mi, ti := initXAConnTestResource(t)
defer func() {
CleanTxHooks()
db.Close()
ctrl.Finish()
}()

t.Run("tx-local", func(t *testing.T) {
tx, err := db.Begin()
assert.NoError(t, err)

mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, "", execCtx.TxCtx.XID)
assert.Equal(t, types.Local, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

_, err = tx.ExecContext(tm.InitSeataContext(context.Background()), "SELECT * FROM user")
assert.NoError(t, err)

err = tx.Commit()
assert.NoError(t, err)

assert.Equal(t, int32(1), atomic.LoadInt32(&comitCnt))
})

t.Run("tx-local-context", func(t *testing.T) {
tx, err := db.BeginTx(context.Background(), &sql.TxOptions{})
assert.NoError(t, err)

mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, "", execCtx.TxCtx.XID)
assert.Equal(t, types.Local, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

_, err = tx.ExecContext(tm.InitSeataContext(context.Background()), "SELECT * FROM user")
assert.NoError(t, err)

err = tx.Commit()
assert.NoError(t, err)

assert.Equal(t, int32(1), atomic.LoadInt32(&comitCnt))
})

t.Run("tx-xa-context", func(t *testing.T) {
ctx := tm.InitSeataContext(context.Background())
tm.SetXID(ctx, uuid.NewString())
tx, err := db.BeginTx(ctx, &sql.TxOptions{})
assert.NoError(t, err)

mi.before = func(_ context.Context, execCtx *types.ExecContext) {
assert.Equal(t, tm.GetXID(ctx), execCtx.TxCtx.XID)
assert.Equal(t, types.XAMode, execCtx.TxCtx.TransactionMode)
}

var comitCnt int32
ti.beforeCommit = func(tx *Tx) {
atomic.AddInt32(&comitCnt, 1)
}

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

_, err = tx.ExecContext(context.Background(), "SELECT * FROM user")
assert.NoError(t, err)

err = tx.Commit()
assert.NoError(t, err)

assert.Equal(t, int32(1), atomic.LoadInt32(&comitCnt))
})

}

+ 0
- 139
pkg/datasource/sql/connector.go View File

@@ -1,139 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"database/sql/driver"
"sync"

"github.com/go-sql-driver/mysql"

"seata.apache.org/seata-go/pkg/datasource/sql/types"
)

type seataATConnector struct {
*seataConnector
transType types.TransactionMode
}

func (c *seataATConnector) Connect(ctx context.Context) (driver.Conn, error) {
conn, err := c.seataConnector.Connect(ctx)
if err != nil {
return nil, err
}

_conn, _ := conn.(*Conn)

return &ATConn{
Conn: _conn,
}, nil
}

func (c *seataATConnector) Driver() driver.Driver {
return &seataATDriver{
seataDriver: c.seataConnector.Driver().(*seataDriver),
}
}

type seataXAConnector struct {
*seataConnector
transType types.TransactionMode
}

func (c *seataXAConnector) Connect(ctx context.Context) (driver.Conn, error) {
conn, err := c.seataConnector.Connect(ctx)
if err != nil {
return nil, err
}

_conn, _ := conn.(*Conn)

return &XAConn{
Conn: _conn,
}, nil
}

func (c *seataXAConnector) Driver() driver.Driver {
return &seataXADriver{
seataDriver: c.seataConnector.Driver().(*seataDriver),
}
}

// A Connector represents a driver in a fixed configuration
// and can create any number of equivalent Conns for use
// by multiple goroutines.
//
// A Connector can be passed to sql.OpenDB, to allow drivers
// to implement their own sql.DB constructors, or returned by
// DriverContext's OpenConnector method, to allow drivers
// access to context and to avoid repeated parsing of driver
// configuration.
//
// If a Connector implements io.Closer, the sql package's DB.Close
// method will call Close and return error (if any).
type seataConnector struct {
transType types.TransactionMode
res *DBResource
once sync.Once
driver driver.Driver
target driver.Connector
cfg *mysql.Config
}

// Connect returns a connection to the database.
// Connect may return a cached connection (one previously
// closed), but doing so is unnecessary; the sql package
// maintains a pool of idle connections for efficient re-use.
//
// The provided context.Context is for dialing purposes only
// (see net.DialContext) and should not be stored or used for
// other purposes. A default timeout should still be used
// when dialing as a connection pool may call Connect
// asynchronously to any query.
//
// The returned connection is only used by one goroutine at a
// time.
func (c *seataConnector) Connect(ctx context.Context) (driver.Conn, error) {
conn, err := c.target.Connect(ctx)
if err != nil {
return nil, err
}
return &Conn{
targetConn: conn,
res: c.res,
txCtx: types.NewTxCtx(),
autoCommit: true,
dbName: c.cfg.DBName,
dbType: types.DBTypeMySQL,
}, nil
}

// Driver returns the underlying Driver of the Connector,
// mainly to maintain compatibility with the Driver method
// on sql.DB.
func (c *seataConnector) Driver() driver.Driver {
c.once.Do(func() {
d := c.target.Driver()
c.driver = d
})

return &seataDriver{
target: c.driver,
}
}

+ 0
- 140
pkg/datasource/sql/connector_test.go View File

@@ -1,140 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sql

import (
"context"
"database/sql"
"database/sql/driver"
"reflect"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"

"seata.apache.org/seata-go/pkg/datasource/sql/mock"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/util/reflectx"
)

type initConnectorFunc func(t *testing.T, ctrl *gomock.Controller) driver.Connector

func initMockConnector(t *testing.T, ctrl *gomock.Controller) driver.Connector {
mockConn := mock.NewMockTestDriverConn(ctrl)

connector := mock.NewMockTestDriverConnector(ctrl)
connector.EXPECT().Connect(gomock.Any()).AnyTimes().Return(mockConn, nil)
mockConn.EXPECT().QueryContext(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
rows := &mysqlMockRows{}
rows.data = [][]interface{}{
{"8.0.29"},
}
return rows, nil
})
return connector
}

func initMockAtConnector(t *testing.T, ctrl *gomock.Controller, db *sql.DB, f initConnectorFunc) driver.Connector {
v := reflect.ValueOf(db)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

field := v.FieldByName("connector")
fieldVal := reflectx.GetUnexportedField(field)

atConnector, ok := fieldVal.(*seataATConnector)
assert.True(t, ok, "need return seata at connector")

v = reflect.ValueOf(atConnector)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
reflectx.SetUnexportedField(v.FieldByName("target"), f(t, ctrl))

return fieldVal.(driver.Connector)
}

func Test_seataATConnector_Connect(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockMgr := initMockResourceManager(branch.BranchTypeAT, ctrl)
_ = mockMgr

db, err := sql.Open(SeataATMySQLDriver, "root:seata_go@tcp(127.0.0.1:3306)/seata_go_test?multiStatements=true")
if err != nil {
t.Fatal(err)
}

defer db.Close()

proxyConnector := initMockAtConnector(t, ctrl, db, initMockConnector)
conn, err := proxyConnector.Connect(context.Background())
assert.NoError(t, err)

atConn, ok := conn.(*ATConn)
assert.True(t, ok, "need return seata at connection")
assert.True(t, atConn.txCtx.TransactionMode == types.Local, "init need local tx")
}

func initMockXaConnector(t *testing.T, ctrl *gomock.Controller, db *sql.DB, f initConnectorFunc) driver.Connector {
v := reflect.ValueOf(db)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

field := v.FieldByName("connector")
fieldVal := reflectx.GetUnexportedField(field)

atConnector, ok := fieldVal.(*seataXAConnector)
assert.True(t, ok, "need return seata xa connector")

v = reflect.ValueOf(atConnector)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
reflectx.SetUnexportedField(v.FieldByName("target"), f(t, ctrl))

return fieldVal.(driver.Connector)
}

func Test_seataXAConnector_Connect(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockMgr := initMockResourceManager(branch.BranchTypeXA, ctrl)
_ = mockMgr

db, err := sql.Open(SeataXAMySQLDriver, "root:seata_go@tcp(127.0.0.1:3306)/seata_go_test?multiStatements=true")
if err != nil {
t.Fatal(err)
}

defer db.Close()

proxyConnector := initMockXaConnector(t, ctrl, db, initMockConnector)
conn, err := proxyConnector.Connect(context.Background())
assert.NoError(t, err)

xaConn, ok := conn.(*XAConn)
assert.True(t, ok, "need return seata xa connection")
assert.True(t, xaConn.txCtx.TransactionMode == types.Local, "init need local tx")
}

+ 0
- 188
pkg/datasource/sql/datasource/base/meta_cache.go View File

@@ -1,188 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package base

import (
"context"
"database/sql"
"fmt"
"sync"
"time"

"github.com/go-sql-driver/mysql"

"seata.apache.org/seata-go/pkg/datasource/sql/types"
)

type (
// trigger
trigger interface {
LoadOne(ctx context.Context, dbName string, table string, conn *sql.Conn) (*types.TableMeta, error)

LoadAll(ctx context.Context, dbName string, conn *sql.Conn, tables ...string) ([]types.TableMeta, error)
}

entry struct {
value types.TableMeta
lastAccess time.Time
}
)

// BaseTableMetaCache
type BaseTableMetaCache struct {
lock sync.RWMutex
expireDuration time.Duration
capity int32
size int32
cache map[string]*entry
cancel context.CancelFunc
trigger trigger
db *sql.DB
cfg *mysql.Config
}

// NewBaseCache
func NewBaseCache(capity int32, expireDuration time.Duration, trigger trigger, db *sql.DB, cfg *mysql.Config) *BaseTableMetaCache {
ctx, cancel := context.WithCancel(context.Background())

c := &BaseTableMetaCache{
lock: sync.RWMutex{},
capity: capity,
size: 0,
expireDuration: expireDuration,
cache: map[string]*entry{},
cancel: cancel,
trigger: trigger,
cfg: cfg,
db: db,
}

c.Init(ctx)

return c
}

// init
func (c *BaseTableMetaCache) Init(ctx context.Context) error {
go c.refresh(ctx)
go c.scanExpire(ctx)

return nil
}

// refresh
func (c *BaseTableMetaCache) refresh(ctx context.Context) {
f := func() {
if c.db == nil || c.cfg == nil || c.cache == nil || len(c.cache) == 0 {
return
}

tables := make([]string, 0, len(c.cache))
for table := range c.cache {
tables = append(tables, table)
}
conn, err := c.db.Conn(ctx)
if err != nil {
return
}
v, err := c.trigger.LoadAll(ctx, c.cfg.DBName, conn, tables...)
if err != nil {
return
}

c.lock.Lock()
defer c.lock.Unlock()

for i := range v {
tm := v[i]
if _, ok := c.cache[tm.TableName]; ok {
c.cache[tm.TableName] = &entry{
value: tm,
}
}
}
}

f()

ticker := time.NewTicker(time.Duration(1 * time.Minute))
defer ticker.Stop()
for range ticker.C {
f()
}
}

// scanExpire
func (c *BaseTableMetaCache) scanExpire(ctx context.Context) {
ticker := time.NewTicker(c.expireDuration)
defer ticker.Stop()
for range ticker.C {

f := func() {
c.lock.Lock()
defer c.lock.Unlock()

cur := time.Now()
for k := range c.cache {
entry := c.cache[k]

if cur.Sub(entry.lastAccess) > c.expireDuration {
delete(c.cache, k)
}
}
}

f()
}
}

// GetTableMeta
func (c *BaseTableMetaCache) GetTableMeta(ctx context.Context, dbName, tableName string, conn *sql.Conn) (types.TableMeta, error) {
c.lock.Lock()
defer c.lock.Unlock()

defer conn.Close()
v, ok := c.cache[tableName]
if !ok {
meta, err := c.trigger.LoadOne(ctx, dbName, tableName, conn)
if err != nil {
return types.TableMeta{}, err
}

if meta != nil && !meta.IsEmpty() {
c.cache[tableName] = &entry{
value: *meta,
lastAccess: time.Now(),
}

return *meta, nil
}

return types.TableMeta{}, fmt.Errorf("not found table metadata")
}

v.lastAccess = time.Now()
c.cache[tableName] = v

return v.value, nil
}

func (c *BaseTableMetaCache) Destroy() error {
c.cancel()
return nil
}

+ 0
- 288
pkg/datasource/sql/datasource/base/meta_cache_test.go View File

@@ -1,288 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package base

import (
"context"
"database/sql"
"sync"
"testing"
"time"

"github.com/DATA-DOG/go-sqlmock"
"github.com/agiledragon/gomonkey/v2"
"github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/assert"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/testdata"
)

var (
capacity int32 = 1024
EexpireTime = 15 * time.Minute
tableMetaOnce sync.Once
)

type mockTrigger struct {
}

// LoadOne simulates loading table metadata, including id, name, and age columns.
func (m *mockTrigger) LoadOne(ctx context.Context, dbName string, table string, conn *sql.Conn) (*types.TableMeta, error) {

return &types.TableMeta{
TableName: table,
Columns: map[string]types.ColumnMeta{
"id": {ColumnName: "id"},
"name": {ColumnName: "name"},
"age": {ColumnName: "age"},
},
Indexs: map[string]types.IndexMeta{
"id": {
Name: "PRIMARY",
IType: types.IndexTypePrimaryKey,
Columns: []types.ColumnMeta{{ColumnName: "id"}},
},
"id_name_age": {
Name: "name_age_idx",
IType: types.IndexUnique,
Columns: []types.ColumnMeta{{ColumnName: "name"}, {ColumnName: "age"}},
},
},
ColumnNames: []string{"id", "name", "age"},
}, nil
}

func (m *mockTrigger) LoadAll(ctx context.Context, dbName string, conn *sql.Conn, tables ...string) ([]types.TableMeta, error) {
return nil, nil
}

func TestBaseTableMetaCache_refresh(t *testing.T) {
type fields struct {
lock sync.RWMutex
expireDuration time.Duration
capity int32
size int32
cache map[string]*entry
cancel context.CancelFunc
trigger trigger
db *sql.DB
cfg *mysql.Config
}
type args struct {
ctx context.Context
}
ctx, cancel := context.WithCancel(context.Background())
tests := []struct {
name string
fields fields
args args
want types.TableMeta
}{
{
name: "test1",
fields: fields{
lock: sync.RWMutex{},
capity: capacity,
size: 0,
expireDuration: EexpireTime,
cache: map[string]*entry{
"test": {
value: types.TableMeta{},
lastAccess: time.Now(),
},
},
cancel: cancel,
trigger: &mockTrigger{},
cfg: &mysql.Config{},
db: &sql.DB{},
},
args: args{ctx: ctx},
want: testdata.MockWantTypesMeta("test"),
},
{
name: "test2",
fields: fields{
lock: sync.RWMutex{},
capity: capacity,
size: 0,
expireDuration: EexpireTime,
cache: map[string]*entry{
"TEST": {
value: types.TableMeta{},
lastAccess: time.Now(),
},
},
cancel: cancel,
trigger: &mockTrigger{},
cfg: &mysql.Config{},
db: &sql.DB{},
},
args: args{ctx: ctx},
want: testdata.MockWantTypesMeta("TEST"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

connStub := gomonkey.ApplyMethodFunc(tt.fields.db, "Conn",
func(_ context.Context) (*sql.Conn, error) {
return &sql.Conn{}, nil
})

defer connStub.Reset()

loadAllStub := gomonkey.ApplyMethodFunc(tt.fields.trigger, "LoadAll",
func(_ context.Context, _ string, _ *sql.Conn, _ ...string) ([]types.TableMeta, error) {
return []types.TableMeta{tt.want}, nil
})

defer loadAllStub.Reset()

c := &BaseTableMetaCache{
lock: tt.fields.lock,
expireDuration: tt.fields.expireDuration,
capity: tt.fields.capity,
size: tt.fields.size,
cache: tt.fields.cache,
cancel: tt.fields.cancel,
trigger: tt.fields.trigger,
db: tt.fields.db,
cfg: tt.fields.cfg,
}
go c.refresh(tt.args.ctx)
time.Sleep(time.Second * 3)
c.lock.RLock()
defer c.lock.RUnlock()
assert.Equal(t, c.cache[func() string {
if tt.name == "test2" {
return "TEST"
}
return "test"
}()].value, tt.want)
})
}
}

func TestBaseTableMetaCache_GetTableMeta(t *testing.T) {
var (
tableMeta1 types.TableMeta
tableMeta2 types.TableMeta
columns = make(map[string]types.ColumnMeta)
index = make(map[string]types.IndexMeta)
index2 = make(map[string]types.IndexMeta)
columnMeta1 []types.ColumnMeta
columnMeta2 []types.ColumnMeta
ColumnNames []string
)
columnId := types.ColumnMeta{
ColumnDef: nil,
ColumnName: "id",
}
columnName := types.ColumnMeta{
ColumnDef: nil,
ColumnName: "name",
}
columnAge := types.ColumnMeta{
ColumnDef: nil,
ColumnName: "age",
}
columns["id"] = columnId
columns["name"] = columnName
columns["age"] = columnAge
columnMeta1 = append(columnMeta1, columnId)
columnMeta2 = append(columnMeta2, columnName, columnAge)
index["id"] = types.IndexMeta{
Name: "PRIMARY",
IType: types.IndexTypePrimaryKey,
Columns: columnMeta1,
}
index["id_name_age"] = types.IndexMeta{
Name: "name_age_idx",
IType: types.IndexUnique,
Columns: columnMeta2,
}

ColumnNames = []string{"id", "name", "age"}
tableMeta1 = types.TableMeta{
TableName: "t_user1",
Columns: columns,
Indexs: index,
ColumnNames: ColumnNames,
}

index2["id_name_age"] = types.IndexMeta{
Name: "name_age_idx",
IType: types.IndexUnique,
Columns: columnMeta2,
}

tableMeta2 = types.TableMeta{
TableName: "T_USER2",
Columns: columns,
Indexs: index2,
ColumnNames: ColumnNames,
}
tests := []types.TableMeta{tableMeta1, tableMeta2}
// Use sqlmock to simulate a database connection
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Failed to create sqlmock: %v", err)
}
defer db.Close()
for _, tt := range tests {
t.Run(tt.TableName, func(t *testing.T) {
mockTrigger := &mockTrigger{}
// Mock a query response
mock.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "age"}))
// Create a mock database connection
conn, err := db.Conn(context.Background())
if err != nil {
t.Fatalf("Failed to get connection: %v", err)
}
defer conn.Close()
cache := &BaseTableMetaCache{
trigger: mockTrigger,
cache: map[string]*entry{
"t_user1": {
value: tableMeta1,
lastAccess: time.Now(),
},
"T_USER2": {
value: tableMeta2,
lastAccess: time.Now(),
},
},
lock: sync.RWMutex{},
}

meta, _ := cache.GetTableMeta(context.Background(), "db", tt.TableName, conn)

if meta.TableName != tt.TableName {
t.Errorf("GetTableMeta() got TableName = %v, want %v", meta.TableName, tt.TableName)
}
// Ensure the retrieved table is cached
cache.lock.RLock()
_, cached := cache.cache[tt.TableName]
cache.lock.RUnlock()

if !cached {
t.Errorf("GetTableMeta() got TableName = %v, want %v", meta.TableName, tt.TableName)
}
})
}
}

+ 0
- 125
pkg/datasource/sql/datasource/datasource_manager.go View File

@@ -1,125 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package datasource

import (
"context"
"database/sql"
"fmt"
"sync"

"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/rm"
)

var (
atOnce sync.Once
tableMetaCacheMap = map[types.DBType]TableMetaCache{}
)

// RegisterTableCache register the table meta cache for at and xa
func RegisterTableCache(dbType types.DBType, tableMetaCache TableMetaCache) {
tableMetaCacheMap[dbType] = tableMetaCache
}

func GetTableCache(dbType types.DBType) TableMetaCache {
return tableMetaCacheMap[dbType]
}

func GetDataSourceManager(branchType branch.BranchType) DataSourceManager {
resourceManager := rm.GetRmCacheInstance().GetResourceManager(branchType)
if resourceManager == nil {
return nil
}
if d, ok := resourceManager.(DataSourceManager); ok {
return d
}
return nil
}

type DataSourceManager interface {
rm.ResourceManager
CreateTableMetaCache(ctx context.Context, resID string, dbType types.DBType, db *sql.DB) (TableMetaCache, error)
}

type entry struct {
db *sql.DB
metaCache TableMetaCache
}

// BasicSourceManager the basic source manager for xa and at
type BasicSourceManager struct {
lock sync.RWMutex
// tableMetaCache
// todo do not put meta cache here
tableMetaCache map[string]*entry
}

func NewBasicSourceManager() *BasicSourceManager {
return &BasicSourceManager{
tableMetaCache: make(map[string]*entry, 0),
}
}

// RegisterResource register a model.Resource to be managed by model.Resource Manager
func (dm *BasicSourceManager) RegisterResource(resource rm.Resource) error {
err := rm.GetRMRemotingInstance().RegisterResource(resource)
if err != nil {
return err
}
return nil
}

func (dm *BasicSourceManager) UnregisterResource(resource rm.Resource) error {
return fmt.Errorf("unsupport unregister resource")
}

// CreateTableMetaCache create a table meta cache
func (dm *BasicSourceManager) CreateTableMetaCache(ctx context.Context, resID string, dbType types.DBType, db *sql.DB) (TableMetaCache, error) {
dm.lock.Lock()
defer dm.lock.Unlock()

res, err := buildResource(ctx, dbType, db)
if err != nil {
return nil, err
}

dm.tableMetaCache[resID] = res
return res.metaCache, err
}

// TableMetaCache tables metadata cache, default is open
type TableMetaCache interface {
Init(ctx context.Context, conn *sql.DB) error
GetTableMeta(ctx context.Context, dbName, table string) (*types.TableMeta, error)
Destroy() error
}

// buildResource
func buildResource(ctx context.Context, dbType types.DBType, db *sql.DB) (*entry, error) {
cache := tableMetaCacheMap[dbType]
if err := cache.Init(ctx, db); err != nil {
return nil, err
}

return &entry{
db: db,
metaCache: cache,
}, nil
}

+ 0
- 79
pkg/datasource/sql/datasource/mysql/meta_cache.go View File

@@ -1,79 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package mysql

import (
"context"
"database/sql"
"fmt"
"sync"
"time"

"github.com/go-sql-driver/mysql"

"seata.apache.org/seata-go/pkg/datasource/sql/datasource/base"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
)

var (
capacity int32 = 1024
EexpireTime = 15 * time.Minute
tableMetaOnce sync.Once
)

type TableMetaCache struct {
tableMetaCache *base.BaseTableMetaCache
db *sql.DB
}

func NewTableMetaInstance(db *sql.DB, cfg *mysql.Config) *TableMetaCache {
tableMetaInstance := &TableMetaCache{
tableMetaCache: base.NewBaseCache(capacity, EexpireTime, NewMysqlTrigger(), db, cfg),
db: db,
}
return tableMetaInstance
}

// Init
func (c *TableMetaCache) Init(ctx context.Context, conn *sql.DB) error {
return nil
}

// GetTableMeta get table info from cache or information schema
func (c *TableMetaCache) GetTableMeta(ctx context.Context, dbName, tableName string) (*types.TableMeta, error) {
if tableName == "" {
return nil, fmt.Errorf("table name is empty")
}

conn, err := c.db.Conn(ctx)
if err != nil {
return nil, err
}

tableMeta, err := c.tableMetaCache.GetTableMeta(ctx, dbName, tableName, conn)
if err != nil {
return nil, err
}

return &tableMeta, nil
}

// Destroy
func (c *TableMetaCache) Destroy() error {
return nil
}

+ 0
- 222
pkg/datasource/sql/datasource/mysql/trigger.go View File

@@ -1,222 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package mysql

import (
"context"
"database/sql"
"fmt"
"strings"

"github.com/pkg/errors"

"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/datasource/sql/undo/executor"
)

type mysqlTrigger struct {
}

func NewMysqlTrigger() *mysqlTrigger {
return &mysqlTrigger{}
}

// LoadOne get table meta column and index
func (m *mysqlTrigger) LoadOne(ctx context.Context, dbName string, tableName string, conn *sql.Conn) (*types.TableMeta, error) {
tableMeta := types.TableMeta{
TableName: tableName,
Columns: make(map[string]types.ColumnMeta),
Indexs: make(map[string]types.IndexMeta),
}

columnMetas, err := m.getColumnMetas(ctx, dbName, tableName, conn)
if err != nil {
return nil, errors.Wrapf(err, "Could not found any columnMeta in the table: %s", tableName)
}

var columns []string
for _, columnMeta := range columnMetas {
tableMeta.Columns[columnMeta.ColumnName] = columnMeta
columns = append(columns, columnMeta.ColumnName)
}
tableMeta.ColumnNames = columns

indexes, err := m.getIndexes(ctx, dbName, tableName, conn)
if err != nil {
return nil, errors.Wrapf(err, "Could not found any index in the table: %s", tableName)
}
for _, index := range indexes {
col := tableMeta.Columns[index.ColumnName]
idx, ok := tableMeta.Indexs[index.Name]
if ok {
idx.Columns = append(idx.Columns, col)
tableMeta.Indexs[index.Name] = idx
} else {
index.Columns = append(index.Columns, col)
tableMeta.Indexs[index.Name] = index
}
}
if len(tableMeta.Indexs) == 0 {
return nil, fmt.Errorf("could not found any index in the table: %s", tableName)
}

return &tableMeta, nil
}

// LoadAll
func (m *mysqlTrigger) LoadAll(ctx context.Context, dbName string, conn *sql.Conn, tables ...string) ([]types.TableMeta, error) {
var tableMetas []types.TableMeta
for _, tableName := range tables {
tableMeta, err := m.LoadOne(ctx, dbName, tableName, conn)
if err != nil {
continue
}
tableMetas = append(tableMetas, *tableMeta)
}
return tableMetas, nil
}

// getColumnMetas get tableMeta column
func (m *mysqlTrigger) getColumnMetas(ctx context.Context, dbName string, table string, conn *sql.Conn) ([]types.ColumnMeta, error) {
table = executor.DelEscape(table, types.DBTypeMySQL)
var columnMetas []types.ColumnMeta

columnMetaSql := "SELECT `TABLE_NAME`, `TABLE_SCHEMA`, `COLUMN_NAME`, `DATA_TYPE`, `COLUMN_TYPE`, `COLUMN_KEY`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `EXTRA` FROM INFORMATION_SCHEMA.COLUMNS WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
stmt, err := conn.PrepareContext(ctx, columnMetaSql)
if err != nil {
return nil, err
}
defer stmt.Close()

rows, err := stmt.Query(dbName, table)
if err != nil {
return nil, err
}
defer rows.Close()

for rows.Next() {
var (
tableName string
tableSchema string
columnName string
dataType string
columnType string
columnKey string
isNullable string
columnDefault []byte
extra string
)

columnMeta := types.ColumnMeta{}
if err = rows.Scan(
&tableName,
&tableSchema,
&columnName,
&dataType,
&columnType,
&columnKey,
&isNullable,
&columnDefault,
&extra); err != nil {
return nil, err
}

columnMeta.Schema = tableSchema
columnMeta.Table = tableName
columnMeta.ColumnName = strings.Trim(columnName, "` ")
columnMeta.DatabaseType = types.GetSqlDataType(dataType)
columnMeta.DatabaseTypeString = dataType
columnMeta.ColumnType = columnType
columnMeta.ColumnKey = columnKey
if strings.ToLower(isNullable) == "yes" {
columnMeta.IsNullable = 1
} else {
columnMeta.IsNullable = 0
}
columnMeta.ColumnDef = columnDefault
columnMeta.Extra = extra
columnMeta.Autoincrement = strings.Contains(strings.ToLower(extra), "auto_increment")
columnMetas = append(columnMetas, columnMeta)
}
if err := rows.Err(); err != nil {
return nil, err
}
if len(columnMetas) == 0 {
return nil, fmt.Errorf("can't find column")
}

return columnMetas, nil
}

// getIndex get tableMetaIndex
func (m *mysqlTrigger) getIndexes(ctx context.Context, dbName string, tableName string, conn *sql.Conn) ([]types.IndexMeta, error) {
tableName = executor.DelEscape(tableName, types.DBTypeMySQL)
result := make([]types.IndexMeta, 0)

indexMetaSql := "SELECT `INDEX_NAME`, `COLUMN_NAME`, `NON_UNIQUE` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
stmt, err := conn.PrepareContext(ctx, indexMetaSql)
if err != nil {
return nil, err
}
defer stmt.Close()
rows, err := stmt.Query(dbName, tableName)
if err != nil {
return nil, err
}
defer rows.Close()

for rows.Next() {
var (
indexName string
columnName string
nonUnique int64
)

err = rows.Scan(&indexName, &columnName, &nonUnique)
if err != nil {
return nil, err
}

index := types.IndexMeta{
Schema: dbName,
Table: tableName,
Name: indexName,
ColumnName: columnName,
Columns: make([]types.ColumnMeta, 0),
}

if nonUnique == 1 {
index.NonUnique = true
}

if "primary" == strings.ToLower(indexName) {
index.IType = types.IndexTypePrimaryKey
} else if !index.NonUnique {
index.IType = types.IndexUnique
} else {
index.IType = types.IndexNormal
}

result = append(result, index)

}
if err := rows.Err(); err != nil {
return nil, err
}
return result, nil
}

+ 0
- 190
pkg/datasource/sql/datasource/mysql/trigger_test.go View File

@@ -1,190 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package mysql

import (
"context"
"database/sql"
"testing"

"github.com/agiledragon/gomonkey/v2"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"seata.apache.org/seata-go/testdata"

"seata.apache.org/seata-go/pkg/datasource/sql/mock"
"seata.apache.org/seata-go/pkg/datasource/sql/types"
"seata.apache.org/seata-go/pkg/protocol/branch"
"seata.apache.org/seata-go/pkg/rm"
)

func initMockIndexMeta() []types.IndexMeta {
return []types.IndexMeta{
{
IType: types.IndexTypePrimaryKey,
ColumnName: "id",
Columns: []types.ColumnMeta{
{
ColumnName: "id",
DatabaseType: types.GetSqlDataType("BIGINT"),
},
},
},
}
}

func initMockColumnMeta() []types.ColumnMeta {
return []types.ColumnMeta{
{
ColumnName: "id",
},
{
ColumnName: "name",
},
}
}

func initGetIndexesStub(m *mysqlTrigger, indexMeta []types.IndexMeta) *gomonkey.Patches {
getIndexesStub := gomonkey.ApplyPrivateMethod(m, "getIndexes",
func(_ *mysqlTrigger, ctx context.Context, dbName string, tableName string, conn *sql.Conn) ([]types.IndexMeta, error) {
return indexMeta, nil
})
return getIndexesStub
}

func initGetColumnMetasStub(m *mysqlTrigger, columnMeta []types.ColumnMeta) *gomonkey.Patches {
getColumnMetasStub := gomonkey.ApplyPrivateMethod(m, "getColumnMetas",
func(_ *mysqlTrigger, ctx context.Context, dbName string, table string, conn *sql.Conn) ([]types.ColumnMeta, error) {
return columnMeta, nil
})
return getColumnMetasStub
}

func Test_mysqlTrigger_LoadOne(t *testing.T) {
wantTableMeta := testdata.MockWantTypesMeta("test")
type args struct {
ctx context.Context
dbName string
tableName string
conn *sql.Conn
}
tests := []struct {
name string
args args
columnMeta []types.ColumnMeta
indexMeta []types.IndexMeta
wantTableMeta *types.TableMeta
}{
{
name: "1",
args: args{ctx: context.Background(), dbName: "dbName", tableName: "test", conn: nil},
indexMeta: initMockIndexMeta(),
columnMeta: initMockColumnMeta(),
wantTableMeta: &wantTableMeta,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &mysqlTrigger{}

getColumnMetasStub := initGetColumnMetasStub(m, tt.columnMeta)
defer getColumnMetasStub.Reset()

getIndexesStub := initGetIndexesStub(m, tt.indexMeta)
defer getIndexesStub.Reset()

got, err := m.LoadOne(tt.args.ctx, tt.args.dbName, tt.args.tableName, tt.args.conn)
if err != nil {
t.Errorf("LoadOne() error = %v", err)
return
}

assert.Equal(t, tt.wantTableMeta, got)
})
}
}

func initMockResourceManager(branchType branch.BranchType, ctrl *gomock.Controller) *mock.MockDataSourceManager {
mockResourceMgr := mock.NewMockDataSourceManager(ctrl)
mockResourceMgr.SetBranchType(branchType)
mockResourceMgr.EXPECT().BranchRegister(gomock.Any(), gomock.Any()).AnyTimes().Return(int64(0), nil)
rm.GetRmCacheInstance().RegisterResourceManager(mockResourceMgr)
mockResourceMgr.EXPECT().RegisterResource(gomock.Any()).AnyTimes().Return(nil)
mockResourceMgr.EXPECT().CreateTableMetaCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, nil)
return mockResourceMgr
}

func Test_mysqlTrigger_LoadAll(t *testing.T) {
sql.Register("seata-at-mysql", &mock.MockTestDriver{})

ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockMgr := initMockResourceManager(branch.BranchTypeAT, ctrl)
_ = mockMgr

conn := sql.Conn{}
type args struct {
ctx context.Context
dbName string
conn *sql.Conn
tables []string
}
tests := []struct {
name string
args args
columnMeta []types.ColumnMeta
indexMeta []types.IndexMeta
want []types.TableMeta
}{
{
name: "test-01",
args: args{
ctx: nil,
dbName: "dbName",
conn: &conn,
tables: []string{
"test_01",
"test_02",
},
},
indexMeta: initMockIndexMeta(),
columnMeta: initMockColumnMeta(),
want: []types.TableMeta{testdata.MockWantTypesMeta("test_01"), testdata.MockWantTypesMeta("test_02")},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &mysqlTrigger{}

getColumnMetasStub := initGetColumnMetasStub(m, tt.columnMeta)
defer getColumnMetasStub.Reset()

getIndexesStub := initGetIndexesStub(m, tt.indexMeta)
defer getIndexesStub.Reset()

got, err := m.LoadAll(tt.args.ctx, tt.args.dbName, tt.args.conn, tt.args.tables...)
if err != nil {
t.Errorf("LoadAll() error = %v", err)
return
}

assert.Equal(t, tt.want, got)
})
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save