Browse Source

Merge pull request #1 from dtm-labs/main

merge latest
alpha
StandHeo 4 years ago
committed by GitHub
parent
commit
987d6634cf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 44
      .github/workflows/release.yml
  2. 10
      .github/workflows/tests.yml
  3. 2
      .gitignore
  4. 12
      README.md
  5. 1
      admin/.env
  6. 0
      admin/.eslintrc.js
  7. 0
      admin/.gitignore
  8. 0
      admin/README.md
  9. 1
      admin/dist/placeholder
  10. 0
      admin/index.html
  11. 3
      admin/package.json
  12. 0
      admin/postcss.config.js
  13. 0
      admin/public/favicon.ico
  14. 0
      admin/src/App.vue
  15. 38
      admin/src/api/api_dtm.ts
  16. 0
      admin/src/assets/css/index.css
  17. 4
      admin/src/components.d.ts
  18. 40
      admin/src/components/Screenfull/index.vue
  19. 2
      admin/src/components/SvgIcon/index.vue
  20. 0
      admin/src/icons/readme.md
  21. 1
      admin/src/icons/svg/fullscreen.svg
  22. 8
      admin/src/icons/svg/logo.svg
  23. 19
      admin/src/layout/aside.vue
  24. 39
      admin/src/layout/components/content.vue
  25. 15
      admin/src/layout/components/header.vue
  26. 0
      admin/src/layout/components/sidebar.vue
  27. 0
      admin/src/layout/index.vue
  28. 33
      admin/src/main.ts
  29. 0
      admin/src/permission.ts
  30. 0
      admin/src/router/asyncRouter.ts
  31. 59
      admin/src/router/index.ts
  32. 0
      admin/src/store/index.ts
  33. 14
      admin/src/store/modules/layout.ts
  34. 9
      admin/src/type/index.d.ts
  35. 0
      admin/src/type/shim.vue.d.ts
  36. 12
      admin/src/type/store/layout.ts
  37. 1
      admin/src/utils/request.ts
  38. 8
      admin/src/utils/util.ts
  39. 59
      admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue
  40. 0
      admin/src/views/Dashboard/GlobalTransactions/UnfinishedTransactions.vue
  41. 117
      admin/src/views/Dashboard/GlobalTransactions/_Components/DialogTransactionDetail.vue
  42. 0
      admin/src/views/Dashboard/Nodes/LivingNodes.vue
  43. 0
      admin/tailwind.config.js
  44. 0
      admin/tsconfig.json
  45. 4
      admin/vite.config.ts
  46. 5
      admin/yarn.lock
  47. 3
      conf.sample.yml
  48. 2
      dashboard/.env.development
  49. 17
      dashboard/src/layout/components/content.vue
  50. 13
      dashboard/src/main.ts
  51. 59
      dashboard/src/router/index.ts
  52. 20
      dtmcli/barrier.go
  53. 7
      dtmcli/dtmimp/db_special.go
  54. 4
      dtmcli/dtmimp/db_special_test.go
  55. 12
      dtmcli/dtmimp/trans_xa_base.go
  56. 16
      dtmcli/dtmimp/utils.go
  57. 4
      dtmgrpc/type.go
  58. 3
      dtmsvr/api.go
  59. 6
      dtmsvr/api_http.go
  60. 13
      dtmsvr/config/config.go
  61. 2
      dtmsvr/config/config_utils.go
  62. 21
      dtmsvr/entry/main.go
  63. 41
      dtmsvr/storage/boltdb/boltdb.go
  64. 8
      dtmsvr/storage/redis/redis.go
  65. 2
      dtmsvr/storage/registry/registry.go
  66. 81
      dtmsvr/storage/sql/sql.go
  67. 2
      dtmsvr/storage/store.go
  68. 12
      dtmsvr/storage/trans.go
  69. 22
      dtmsvr/trans_status.go
  70. 16
      dtmsvr/trans_type_saga.go
  71. 4
      dtmsvr/trans_type_tcc.go
  72. 4
      dtmsvr/trans_type_xa.go
  73. 2
      dtmutil/db.go
  74. 4
      dtmutil/utils.go
  75. 13
      go.mod
  76. 100
      go.sum
  77. 6
      helper/.goreleaser.yml
  78. 11
      helper/Dockerfile-release
  79. 73
      helper/README-cn.md
  80. 10
      helper/README-en.md
  81. 16
      helper/bench/svr/http.go
  82. 28
      helper/compose.cloud.yml
  83. 16
      helper/compose.mysql.yml
  84. 13
      helper/compose.postgres.yml
  85. 1
      helper/compose.store.yml
  86. 2
      helper/golint.sh
  87. 2
      helper/test-cover.sh
  88. 71
      main.go
  89. 13
      sqls/dtmsvr.storage.mysql.sql
  90. 30
      sqls/dtmsvr.storage.postgres.sql
  91. 1
      sqls/dtmsvr.storage.tdsql.sql
  92. 6
      test/api_test.go
  93. 4
      test/base_test.go
  94. 34
      test/busi/barrier.go
  95. 5
      test/busi/base_http.go
  96. 1
      test/busi/base_types.go
  97. 20
      test/busi/busi.go
  98. 3
      test/busi/quick_start.go
  99. 6
      test/common_test.go
  100. 4
      test/dtmsvr_test.go

44
.github/workflows/release.yml

@ -9,8 +9,14 @@ jobs:
name: Release on GitHub name: Release on GitHub
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get Release Version
run: |
export RELEASE_VERSION=${GITHUB_REF#refs/*/}
echo RELEASE_VERSION: ${RELEASE_VERSION}
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> $GITHUB_ENV
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Validates GO releaser config - name: Validates GO releaser config
uses: docker://goreleaser/goreleaser:v1.7.0 uses: docker://goreleaser/goreleaser:v1.7.0
@ -24,7 +30,38 @@ jobs:
env: env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Docker meta - name: Setup node
uses: actions/setup-node@v3
with:
node-version: 14
- name: Build admin
run: |
cd admin
npm install -g yarn
yarn
VITE_ADMIN_VERSION=${{ env.RELEASE_VERSION }} yarn build
cd ..
- name: Scp admin
env:
host: 'ubuntu@en.dtm.pub'
host2: 'ubuntu@dtm.pub'
dest: '/data/dtm-admin/'
run: |
cd admin
echo "${{secrets.DEPLOY_KEY}}" > deploy_key
chmod 600 ./deploy_key
tar -cvzf dist.tar.gz dist
scp -i deploy_key -o StrictHostKeyChecking=no dist.tar.gz ${{env.host}}:${{env.dest}}
ssh -i deploy_key -o StrictHostKeyChecking=no ${{env.host}} 'cd ${{env.dest}} && tar -zvxf dist.tar.gz'
scp -i deploy_key -o StrictHostKeyChecking=no dist.tar.gz ${{env.host2}}:${{env.dest}}
ssh -i deploy_key -o StrictHostKeyChecking=no ${{env.host2}} 'cd ${{env.dest}} && tar -zvxf dist.tar.gz'
rm deploy_key dist.tar.gz
echo > dist/placeholder
cd ..
- name: Create Docker
id: meta id: meta
uses: docker/metadata-action@v3 uses: docker/metadata-action@v3
with: with:
@ -48,9 +85,6 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get Release Version
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Build and push - name: Build and push
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:

10
.github/workflows/tests.yml

@ -28,6 +28,16 @@ jobs:
- /etc/timezone:/etc/timezone:ro - /etc/timezone:/etc/timezone:ro
ports: ports:
- 6379:6379 - 6379:6379
postgres:
image: 'yedf/postgres-xa'
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
env:
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_DB: dtm
ports:
- '5432:5432'
mongo: mongo:
image: 'yedf/mongo-rs' image: 'yedf/mongo-rs'
volumes: volumes:

2
.gitignore

@ -1,7 +1,7 @@
conf.yml conf.yml
*.out *.out
*.log *.log
dist # dist
.idea/** .idea/**
.vscode .vscode
default.etcd default.etcd

12
README.md

@ -15,13 +15,17 @@ DTM is a distributed transaction framework which provides cross-service eventual
<img alt="function-picture" src="https://en.dtm.pub/assets/function.7d5618f8.png" height=250 /> <img alt="function-picture" src="https://en.dtm.pub/assets/function.7d5618f8.png" height=250 />
## Features
## Who's using DTM (partial) ## Who's using DTM (partial)
[Tencent](https://www.tencent.com/) [Tencent](https://en.dtm.pub/other/using.html#tencent)
[Bytedance](https://en.dtm.pub/other/using.html#bytedance)
[Bytedance](https://www.bytedance.com/) [Ivydad](https://en.dtm.pub/other/using.html#ivydad)
[Ivydad](https://ivydad.com) [More](https://en.dtm.pub/other/using.html)
## [Cook Book](https://en.dtm.pub) ## [Cook Book](https://en.dtm.pub)
@ -54,7 +58,7 @@ go run main.go
DtmServer := "http://localhost:36789/api/dtmsvr" DtmServer := "http://localhost:36789/api/dtmsvr"
req := &gin.H{"amount": 30} // micro-service payload req := &gin.H{"amount": 30} // micro-service payload
// DtmServer is the address of DTM micro-service // DtmServer is the address of DTM micro-service
saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). saga := dtmcli.NewSaga(DtmServer, shortuuid.New()).
// add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCom" // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCom"
Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req).
// add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCom" // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCom"

1
admin/.env

@ -0,0 +1 @@
VITE_ADMIN_VERSION="v0.0.0-dev"

0
dashboard/.eslintrc.js → admin/.eslintrc.js

0
dashboard/.gitignore → admin/.gitignore

0
dashboard/README.md → admin/README.md

1
admin/dist/placeholder

@ -0,0 +1 @@

0
dashboard/index.html → admin/index.html

3
dashboard/package.json → admin/package.json

@ -4,7 +4,7 @@
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build && echo > dist/placeholder",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
@ -28,6 +28,7 @@
"postcss-import": "^14.1.0", "postcss-import": "^14.1.0",
"postcss-nested": "^5.0.6", "postcss-nested": "^5.0.6",
"postcss-simple-vars": "^6.0.3", "postcss-simple-vars": "^6.0.3",
"screenfull": "^6.0.1",
"tailwindcss": "^3.0.24", "tailwindcss": "^3.0.24",
"typescript": "^4.5.4", "typescript": "^4.5.4",
"unplugin-vue-components": "^0.19.3", "unplugin-vue-components": "^0.19.3",

0
dashboard/postcss.config.js → admin/postcss.config.js

0
dashboard/public/favicon.ico → admin/public/favicon.ico

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

0
dashboard/src/App.vue → admin/src/App.vue

38
admin/src/api/api_dtm.ts

@ -0,0 +1,38 @@
import { AxiosResponse } from 'axios'
import request from '/@/utils/request'
export interface IListAllTransactionsReq {
limit: number
position?: string
}
export function listAllTransactions<T>(payload: IListAllTransactionsReq): Promise<AxiosResponse<T>> {
return request({
url: '/api/dtmsvr/all',
method: 'get',
params: payload
})
}
export function forceStopTransaction(gid: string): Promise<AxiosResponse> {
return request({
url: '/api/dtmsvr/forceStop',
method: 'post',
data: { gid },
})
}
export function getTransaction<T>(payload: { gid: string }): Promise<AxiosResponse<T>> {
return request({
url: '/api/dtmsvr/query',
method: 'get',
params: payload
})
}
export function getDtmVersion(): Promise<AxiosResponse<any>> {
return request({
url: '/api/dtmsvr/version',
method: 'get',
})
}

0
dashboard/src/assets/css/index.css → admin/src/assets/css/index.css

4
dashboard/src/components.d.ts → admin/src/components.d.ts

@ -5,6 +5,7 @@ import '@vue/runtime-core'
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
AAlert: typeof import('ant-design-vue/es')['Alert']
ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button'] AButton: typeof import('ant-design-vue/es')['Button']
@ -14,11 +15,14 @@ declare module '@vue/runtime-core' {
ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider'] ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
AMenu: typeof import('ant-design-vue/es')['Menu'] AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
ATable: typeof import('ant-design-vue/es')['Table'] ATable: typeof import('ant-design-vue/es')['Table']
ATag: typeof import('ant-design-vue/es')['Tag'] ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
Screenfull: typeof import('./components/Screenfull/index.vue')['default']
SvgIcon: typeof import('./components/SvgIcon/index.vue')['default'] SvgIcon: typeof import('./components/SvgIcon/index.vue')['default']
} }
} }

40
admin/src/components/Screenfull/index.vue

@ -0,0 +1,40 @@
<template>
<div class="hidden-xs-only px-2">
<svg-icon v-if="!isFulScreen" class-name="cursor-pointer" icon-class="svg-fullscreen" @click="changeScreenfull(identity)" />
<svg-icon v-else class-name="cursor-pointer" icon-class="svg-exit-fullscreen" @click="changeScreenfull(identity)" />
</div>
</template>
<script setup lang='ts'>
import { notification } from 'ant-design-vue';
import { onMounted, onUnmounted, ref } from 'vue'
import screenfull from 'screenfull'
const isFulScreen = ref(false)
const changeScreenfull = (identity: string) => {
if (!screenfull.isEnabled) {
notification.open({
message: '浏览器不支持全屏',
type: 'warning'
})
} else if (identity) {
const element = document.getElementById(identity)
screenfull.toggle(element as HTMLElement)
} else {
screenfull.toggle()
}
}
const change = () => {
if (screenfull.isEnabled) isFulScreen.value = screenfull.isFullscreen
}
defineProps({
identity: {
type: String,
default: null
}
})
const emits = defineEmits(['screen'])
onMounted(() => screenfull.isEnabled && screenfull.on('change', change) && emits('screen'))
onUnmounted(() => screenfull.isEnabled && screenfull.off('change', change))
</script>

2
dashboard/src/components/SvgIcon/index.vue → admin/src/components/SvgIcon/index.vue

@ -30,6 +30,8 @@ const styleExternalIcon = () => {
<style lang="postcss" scoped> <style lang="postcss" scoped>
.svg-icon { .svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em; vertical-align: -0.15em;
fill: currentColor; fill: currentColor;
overflow: hidden; overflow: hidden;

0
dashboard/src/icons/readme.md → admin/src/icons/readme.md

1
admin/src/icons/svg/fullscreen.svg

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>

After

Width:  |  Height:  |  Size: 422 B

8
admin/src/icons/svg/logo.svg

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>画板</title>
<g id="画板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<circle id="椭圆形" fill="#FF6400" cx="100" cy="100" r="100"></circle>
<path d="M108.718018,155.765918 C125.450635,155.765918 137.824756,148.87749 145.840381,135.100635 C151.751904,124.880713 154.707666,112.406396 154.707666,97.6776855 C154.707666,91.8663574 154.006299,85.7544434 152.603564,79.3419434 C151.20083,72.9294434 148.570703,66.9678223 144.713184,61.4570801 C139.903809,54.6437988 133.616553,49.959668 125.851416,47.4046875 C121.292529,45.9017578 115.581396,45.1001953 108.718018,45 L108.718018,45 L61,45 L61,155.765918 L108.718018,155.765918 Z M104.8104,136.528418 L83.4687988,136.528418 L83.4687988,64.2375 L104.8104,64.2375 C115.330908,64.2375 122.357104,67.2558838 125.888989,73.2926514 C129.420874,79.3294189 131.186816,87.9837891 131.186816,99.2557617 C131.186816,107.42168 130.009521,114.4604 127.654932,120.371924 C123.346533,131.14292 115.731689,136.528418 104.8104,136.528418 L104.8104,136.528418 Z" id="D" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

19
dashboard/src/layout/aside.vue → admin/src/layout/aside.vue

@ -4,6 +4,7 @@
<Sidebar /> <Sidebar />
</a-layout-sider> </a-layout-sider>
<a-layout style="padding: 0 24px 24px"> <a-layout style="padding: 0 24px 24px">
<div v-if="layout.dtmVersion && layout.dtmVersion != dashVer" style="color:#f00"> !!! admin version: {{dashVer}} != dtm version: {{layout.dtmVersion}}. </div>
<a-breadcrumb style="margin: 16px 0"> <a-breadcrumb style="margin: 16px 0">
<a-breadcrumb-item>{{ mainNav }}</a-breadcrumb-item> <a-breadcrumb-item>{{ mainNav }}</a-breadcrumb-item>
<a-breadcrumb-item>{{ subNav }}</a-breadcrumb-item> <a-breadcrumb-item>{{ subNav }}</a-breadcrumb-item>
@ -26,18 +27,21 @@ import { useLayoutStore } from '../store/modules/layout'
import { IMenubarList } from '../type/store/layout' import { IMenubarList } from '../type/store/layout'
import { findCurrentMenubar } from '../utils/util' import { findCurrentMenubar } from '../utils/util'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { getDtmVersion } from '../api/api_dtm'
const dashVer = import.meta.env.VITE_ADMIN_VERSION
const route = useRoute() const route = useRoute()
const { getMenubar } = useLayoutStore() const layout = useLayoutStore()
const mainNav = computed(() => { const mainNav = computed(() => {
const currentMenubar = findCurrentMenubar(getMenubar.menuList, true) const currentMenubar = findCurrentMenubar(layout.getMenubar.menuList, true)
return currentMenubar?.meta.title return currentMenubar?.meta.title
}) })
const subNav = computed(() => { const subNav = computed(() => {
let subNav = '' let subNav = ''
const currentMenubar = findCurrentMenubar(getMenubar.menuList, true) const currentMenubar = findCurrentMenubar(layout.getMenubar.menuList, true)
currentMenubar.children?.forEach(v => { currentMenubar.children?.forEach(v => {
if (route.path.indexOf(v.path) !== -1) { if (route.path.indexOf(v.path) !== -1) {
subNav = v.meta.title subNav = v.meta.title
@ -49,17 +53,22 @@ const subNav = computed(() => {
const page = computed(() => { const page = computed(() => {
let page = '' let page = ''
const currentMenubar = findCurrentMenubar(getMenubar.menuList, true) const currentMenubar = findCurrentMenubar(layout.getMenubar.menuList, true)
currentMenubar.children?.forEach(v => { currentMenubar.children?.forEach(v => {
v.children?.forEach(vv => { v.children?.forEach(vv => {
if (route.path == vv.path) { if (route.path == vv.path) {
page = vv.meta.title page = vv.meta.title
} }
}) })
}) })
return page return page
}) })
onMounted(() => {
layout.loadDtmVersion()
})
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>

39
admin/src/layout/components/content.vue

@ -0,0 +1,39 @@
<template>
<router-view v-slot="{Component}">
<transition name="fade-transform" mode="out-in">
<keep-alive>
<div>
<a-alert
v-if="errLines[0]"
type="error"
closable
@close="onClose"
>
<template v-slot:description>
<span v-for="ln of errLines">{{ln}} <br/> </span>
</template>
</a-alert>
<component :is="Component" :key="key" />
</div>
</keep-alive>
</transition>
</router-view>
</template>
<script setup lang='ts'>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { sleep } from '/@/utils/util'
import { useLayoutStore } from '/@/store/modules/layout'
const route = useRoute()
const key = computed(() => route.path)
const layoutStore = useLayoutStore()
const errLines = computed(() => layoutStore.globalError.split("\n"))
const onClose = async (e: MouseEvent) => {
await sleep(1000)
layoutStore.setGlobalError("")
}
</script>

15
dashboard/src/layout/components/header.vue → admin/src/layout/components/header.vue

@ -1,7 +1,10 @@
<template> <template>
<div> <div>
<a-layout-header class="header"> <a-layout-header class="header flex">
<svg-icon class="logo" style="width: 36px; height: 36px;" icon-class="svg-dtm" /> <div class="flex items-center logo h-16">
<svg-icon style="width: 36px; height: 36px; margin-right: 84px;" icon-class="svg-logo" />
<span class="text-gray-400 text-lg">DTM admin {{ version }}</span>
</div>
<a-menu <a-menu
v-model:selectedKeys="activeMenu" v-model:selectedKeys="activeMenu"
theme="dark" theme="dark"
@ -21,8 +24,9 @@ import { useLayoutStore } from '/@/store/modules/layout'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const { getMenubar } = useLayoutStore() const { getMenubar, getCurrentVersion } = useLayoutStore()
const firstRedirectPath = '/dashboard' const firstRedirectPath = '/admin'
const version = import.meta.env.VITE_ADMIN_VERSION
const activeMenu = ref([route.meta.activeMenu !== firstRedirectPath ? route.meta.activeMenu : '/']) const activeMenu = ref([route.meta.activeMenu !== firstRedirectPath ? route.meta.activeMenu : '/'])
@ -33,7 +37,6 @@ const onOpenChange = (d:any) => {
<style scoped> <style scoped>
.logo { .logo {
float: left; margin-right: 20px;
margin: 16px 84px 16px 0;
} }
</style> </style>

0
dashboard/src/layout/components/sidebar.vue → admin/src/layout/components/sidebar.vue

0
dashboard/src/layout/index.vue → admin/src/layout/index.vue

33
admin/src/main.ts

@ -0,0 +1,33 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from '/@/router/index'
import { pinia } from '/@/store'
import { useLayoutStore } from '/@/store/modules/layout'
import '/@/permission'
import '/@/assets/css/index.css'
import 'virtual:svg-icons-register'
const app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')
window.onunhandledrejection = (ev: PromiseRejectionEvent) => {
showAlert(ev.reason.stack || ev.reason.message)
}
window.onerror = err => {
if (typeof err === "string") {
return showAlert(err)
}
showAlert(JSON.stringify(err))
}
function showAlert(msg: string) {
let layout = useLayoutStore()
if (!layout.globalError) {
layout.setGlobalError(msg)
}
}

0
dashboard/src/permission.ts → admin/src/permission.ts

0
dashboard/src/router/asyncRouter.ts → admin/src/router/asyncRouter.ts

59
admin/src/router/index.ts

@ -0,0 +1,59 @@
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { IMenubarList } from '../type/store/layout';
import { components } from './asyncRouter';
const Components: IObject<() => Promise<typeof import('*.vue')>> = Object.assign({}, components, {
LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise<typeof import('*.vue')>,
LayoutMain: (() => import('/@/layout/aside.vue')) as unknown as () => Promise<typeof import('*.vue')>
})
export const allowRouter: Array<IMenubarList> = [
{
name: 'Admin',
path: '/',
redirect: '/admin/global-transactions/all',
component: Components['LayoutHeader'],
meta: { title: 'Admin', activeMenu: '/admin' },
children: [
{
// name: 'Nodes',
// path: '/admin/nodes',
// component: Components['LayoutMain'],
// meta: { title: 'Nodes' },
// children: [
// {
// name: 'LivingNodes',
// path: '/admin/nodes/living',
// component: Components['LivingNodes'],
// meta: { title: 'Living Nodes' },
// }
// ]
// }, {
name: 'GlobalTransactions',
path: '/admin/global-transactions',
component: Components['LayoutMain'],
meta: { title: 'Global Transactions' },
children: [
{
name: 'AllTransactions',
path: '/admin/global-transactions/all',
component: Components['AllTransactions'],
meta: { title: 'All Transactions' },
// }, {
// name: 'UnfinishedTransactions',
// path: '/admin/global-transactions/unfinished',
// component: Components['UnfinishedTransactions'],
// meta: { title: 'Unfinished Transactions' },
}
]
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes: allowRouter as RouteRecordRaw[]
})
export default router

0
dashboard/src/store/index.ts → admin/src/store/index.ts

14
dashboard/src/store/modules/layout.ts → admin/src/store/modules/layout.ts

@ -1,17 +1,19 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { allowRouter } from '/@/router'; import { allowRouter } from '/@/router';
import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'; import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout';
import { findCurrentMenubar } from '/@/utils/util'; import { getDtmVersion } from '/@/api/api_dtm';
export const useLayoutStore = defineStore({ export const useLayoutStore = defineStore({
id: 'layout', id: 'layout',
state: ():ILayout => ({ state: (): ILayout => ({
menubar: { menubar: {
menuList: [] menuList: []
}, },
status: { status: {
isLoading: false isLoading: false
}, },
dtmVersion: "",
globalError: "",
}), }),
getters: { getters: {
getMenubar(): IMenubar { getMenubar(): IMenubar {
@ -25,8 +27,16 @@ export const useLayoutStore = defineStore({
setRoutes(data: Array<IMenubarList>): void { setRoutes(data: Array<IMenubarList>): void {
this.menubar.menuList = data this.menubar.menuList = data
}, },
setGlobalError(err: string) {
this.globalError = err
},
concatAllowRoutes(): void { concatAllowRoutes(): void {
allowRouter.reverse().forEach(v => this.menubar.menuList.unshift(v)) allowRouter.reverse().forEach(v => this.menubar.menuList.unshift(v))
}, },
async loadDtmVersion(): Promise<void> {
const { data: { version } } = await getDtmVersion()
this.dtmVersion = version
console.log("dtm version: ", this.dtmVersion)
}
} }
}) })

9
dashboard/src/type/index.d.ts → admin/src/type/index.d.ts

@ -1,4 +1,4 @@
export {} export { }
declare global { declare global {
interface IObject<T> { interface IObject<T> {
[index: string]: T [index: string]: T
@ -7,10 +7,11 @@ declare global {
VITE_APP_TITLE: string VITE_APP_TITLE: string
VITE_PORT: number VITE_PORT: number
VITE_PROXY: string VITE_PROXY: string
VITE_ADMIN_VERSION: string
} }
interface ITable<T = any> { interface ITable<T = any> {
data: Array<T> data: Array<T>
next_position: number, next_position: number,
size: number size: number
} }
} }

0
dashboard/src/type/shim.vue.d.ts → admin/src/type/shim.vue.d.ts

12
dashboard/src/type/store/layout.ts → admin/src/type/store/layout.ts

@ -1,14 +1,16 @@
export interface IMenubar { export interface IMenubar {
menuList: Array<IMenubarList> menuList: Array<IMenubarList>
} }
export interface ILayout { export interface ILayout {
menubar: IMenubar menubar: IMenubar
status: IStatus status: IStatus
dtmVersion: string
globalError: string
} }
export interface IStatus { export interface IStatus {
isLoading: boolean isLoading: boolean
} }
export interface IMenubarList { export interface IMenubarList {
@ -16,7 +18,7 @@ export interface IMenubarList {
id?: number | string id?: number | string
name: string name: string
path: string path: string
redirect?: string redirect?: string
meta: { meta: {
icon?: string icon?: string
title: string title: string

1
dashboard/src/utils/request.ts → admin/src/utils/request.ts

@ -1,7 +1,6 @@
import axios from 'axios' import axios from 'axios'
const request = axios.create({ const request = axios.create({
baseURL: import.meta.env.VITE_APP_API_BASE_URL as string | undefined,
timeout: 60000 timeout: 60000
}) })

8
dashboard/src/utils/util.ts → admin/src/utils/util.ts

@ -1,9 +1,9 @@
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { IMenubarList } from '../type/store/layout'; import { IMenubarList } from '../type/store/layout';
export const findCurrentMenubar = (menuList: IMenubarList[], root?:boolean) => { export const findCurrentMenubar = (menuList: IMenubarList[], root?: boolean) => {
const route = useRoute() const route = useRoute()
let arr:IMenubarList[] | IMenubarList = [] let arr: IMenubarList[] | IMenubarList = []
for (let i = 0; i < menuList.length; i++) { for (let i = 0; i < menuList.length; i++) {
const v = menuList[i]; const v = menuList[i];
const usePath = v.meta.activeMenu || v.redirect || v.path; const usePath = v.meta.activeMenu || v.redirect || v.path;
@ -21,3 +21,7 @@ export const findCurrentMenubar = (menuList: IMenubarList[], root?:boolean) => {
return arr return arr
} }
export const sleep = async (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}

59
dashboard/src/views/Dashboard/GlobalTransactions/AllTransactions.vue → admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue

@ -4,13 +4,14 @@
<template #bodyCell="{column, record}"> <template #bodyCell="{column, record}">
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
<span> <span>
<a-tag :key="record.status" :color="record.status === 'succeed' ? 'green' : 'volcano'">{{ record.status.toUpperCase() }}</a-tag> <a-tag :key="record.status" :color="record.status === 'succeed' ? 'green' : 'volcano'">{{ record.status }}</a-tag>
</span> </span>
</template> </template>
<template v-else-if="column.key === 'action'"> <template v-else-if="column.key === 'action'">
<span> <span>
<a class="mr-2 font-medium">Detail</a> <a class="mr-2 font-medium" @click="handleTransactionDetail(record.gid)">Detail</a>
<a class="text-red-400 font-medium">Stop</a> <a-button danger type="link" :disabled="record.status==='failed' || record.status==='succeed'" @click="handleTransactionStop(record.gid)">ForceStop</a-button>
<!-- <a class="text-red-400 font-medium" @click="handleTransactionStop(record.gid)">ForceStop</a> -->
</span> </span>
</template> </template>
</template> </template>
@ -19,13 +20,15 @@
<a-button type="text" :disabled="!canPrev" @click="handlePrevPage">Previous</a-button> <a-button type="text" :disabled="!canPrev" @click="handlePrevPage">Previous</a-button>
<a-button type="text" :disabled="!canNext" @click="handleNextPage">Next</a-button> <a-button type="text" :disabled="!canNext" @click="handleNextPage">Next</a-button>
</div> </div>
<DialogTransactionDetail ref="transactionDetail" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { IListAllTransactions, listAllTransactions } from '/@/api/dtm' import { IListAllTransactionsReq, listAllTransactions, forceStopTransaction } from '/@/api/api_dtm'
import { ref, onMounted, reactive, computed } from 'vue-demi' import { ref, computed } from 'vue-demi'
import { usePagination } from 'vue-request' import { usePagination } from 'vue-request'
import { TableProps } from 'ant-design-vue/es/vc-table/Table' import DialogTransactionDetail from './_Components/DialogTransactionDetail.vue';
const columns = [ const columns = [
{ {
title: 'GID', title: 'GID',
@ -53,15 +56,11 @@ const columns = [
} }
] ]
const pager = ref([""]) const pages = ref([''])
const currentState = ref(1) const curPage = ref(1)
const canPrev = computed(() => { const canPrev = computed(() => {
if (currentState.value === 1) { return curPage.value > 1
return false;
}
return true;
}) })
const canNext = computed(() => { const canNext = computed(() => {
@ -79,14 +78,14 @@ type Data = {
next_position: string next_position: string
} }
const queryData = (params: IListAllTransactions) => { const queryData = (params: IListAllTransactionsReq) => {
return listAllTransactions<Data>(params) return listAllTransactions<Data>(params)
} }
const { data, run, current, loading, pageSize } = usePagination(queryData, { const { data, run, current, loading, pageSize } = usePagination(queryData, {
defaultParams: [ defaultParams: [
{ {
limit: 100, limit: 10,
} }
], ],
pagination: { pagination: {
@ -97,27 +96,37 @@ const { data, run, current, loading, pageSize } = usePagination(queryData, {
const dataSource = computed(() => data.value?.data.transactions || []) const dataSource = computed(() => data.value?.data.transactions || [])
const handlePrevPage = () => { const handlePrevPage = () => {
currentState.value -= 1; curPage.value -= 1;
const params = { const params = {
limit: 100 limit: pageSize.value,
} position: pages.value[curPage.value] as string
if (pager.value[currentState.value - 1]) {
params.position = pager.value[currentState.value - 1]
} }
run(params) run(params)
} }
const handleNextPage = () => { const handleNextPage = () => {
currentState.value += 1; curPage.value += 1;
if (currentState.value >= 2) { pages.value[curPage.value] = data.value?.data.next_position as string
pager.value[currentState.value - 1] = data.value?.data.next_position
}
run({ run({
position: data.value?.data.next_position, position: data.value?.data.next_position,
limit: 5 limit: pageSize.value,
}) })
} }
const transactionDetail = ref<null | {open:(gid: string) => null}>(null)
const handleTransactionDetail = (gid: string) => {
transactionDetail.value?.open(gid)
}
const handleTransactionStop = async (gid: string) => {
await forceStopTransaction(gid)
run({
position: data.value?.data.next_position,
limit: pageSize.value,
})
}
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>

0
dashboard/src/views/Dashboard/GlobalTransactions/UnfinishedTransactions.vue → admin/src/views/Dashboard/GlobalTransactions/UnfinishedTransactions.vue

117
admin/src/views/Dashboard/GlobalTransactions/_Components/DialogTransactionDetail.vue

@ -0,0 +1,117 @@
<template>
<div>
<a-modal v-model:visible="visible" title="Transaction Detail" width="100%" wrap-class-name="full-modal">
<a-table :columns="columns" :data-source="dataSource" :pagination="false">
<template #bodyCell="{column, record}">
</template>
</a-table>
<div class="mt-10 relative">
<a-textarea id="qs" v-model:value="textVal" :auto-size="{ minRows: 10, maxRows: 10 }" />
<screenfull class="absolute right-2 top-3 z-50" identity="qs" />
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { getTransaction } from '/@/api/api_dtm';
import screenfull from '/@/components/Screenfull/index.vue';
// import VueJsonPretty from 'vue-json-pretty';
// import 'vue-json-pretty/lib/styles.css'
const dataSource = ref<Branches[]>([])
const visible = ref(false)
const textVal = ref('')
const open = async(gid: string) => {
const d = await getTransaction<Data>({gid: gid})
dataSource.value = d.data.branches
textVal.value = JSON.stringify(d.data, null, 2)
visible.value = true
}
const columns = [
{
title: 'GID',
dataIndex: 'gid',
key: 'gid'
}, {
title: 'BranchID',
dataIndex: 'branch_id',
key: 'branch_id'
}, {
title: 'Op',
dataIndex: 'op',
key: 'op'
}, {
title: 'Status',
dataIndex: 'status',
key: 'status'
}, {
title: 'CreateTime',
dataIndex: 'create_time',
key: 'create_time'
}, {
title: 'UpdateTime',
dataIndex: 'update_time',
key: 'update_time'
}
]
type Data = {
branches: {
gid: string
branch_id: string
op: string
status: string
create_time: string
update_time: string
}[]
transactions: {
ID: number
create_time: string
update_time: string
gid: string
trans_type: string
status: string
protocol: string
finish_time: string
options: string
next_cron_interval: number
next_cron_time: string
concurrent: boolean
}
}
interface Branches {
gid: string
branch_id: string
op: string
status: string
create_time: string
update_time: string
}
defineExpose({
open
})
</script>
<style lang="postcss">
.full-modal .ant-modal {
max-width: 100%;
top: 0;
padding-bottom: 0;
margin: 0;
}
.full-modal .ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh);
}
.full-modal .ant-modal-body {
flex: 1;
}
</style>

0
dashboard/src/views/Dashboard/Nodes/LivingNodes.vue → admin/src/views/Dashboard/Nodes/LivingNodes.vue

0
dashboard/tailwind.config.js → admin/tailwind.config.js

0
dashboard/tsconfig.json → admin/tsconfig.json

4
dashboard/vite.config.ts → admin/vite.config.ts

@ -28,8 +28,8 @@ export default ({ }: ConfigEnv): UserConfigExport => {
}) })
], ],
server: { server: {
port: 5000, port: 6789,
base: 'dashboard', base: 'admin',
proxy: { proxy: {
'/api': { '/api': {
target: 'http://localhost:36789', target: 'http://localhost:36789',

5
dashboard/yarn.lock → admin/yarn.lock

@ -2681,6 +2681,11 @@ safe-regex@^1.1.0:
dependencies: dependencies:
ret "~0.1.10" ret "~0.1.10"
screenfull@^6.0.1:
version "6.0.1"
resolved "https://registry.npmmirror.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce"
integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew==
scroll-into-view-if-needed@^2.2.25: scroll-into-view-if-needed@^2.2.25:
version "2.2.29" version "2.2.29"
resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz" resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz"

3
conf.sample.yml

@ -11,6 +11,7 @@
# User: 'root' # User: 'root'
# Password: '' # Password: ''
# Port: 3306 # Port: 3306
# Db: 'dtm'
# Driver: 'boltdb' # default store engine # Driver: 'boltdb' # default store engine
@ -30,8 +31,6 @@
# MaxOpenConns: 500 # MaxOpenConns: 500
# MaxIdleConns: 500 # MaxIdleConns: 500
# ConnMaxLifeTime: 5 # default value is 5 (minutes) # ConnMaxLifeTime: 5 # default value is 5 (minutes)
# TransGlobalTable: 'dtm.trans_global'
# TransBranchOpTable: 'dtm.trans_branch_op'
### flollowing config is only for some Driver ### flollowing config is only for some Driver
# DataExpire: 604800 # Trans data will expire in 7 days. only for redis/boltdb. # DataExpire: 604800 # Trans data will expire in 7 days. only for redis/boltdb.

2
dashboard/.env.development

@ -1,2 +0,0 @@
VITE_APP_API_BASE_URL="/api"
VITE_PROXY=[["/api", "http://localhost:36789"]]

17
dashboard/src/layout/components/content.vue

@ -1,17 +0,0 @@
<template>
<router-view v-slot="{Component}">
<transition name="fade-transform" mode="out-in">
<keep-alive>
<component :is="Component" :key="key" />
</keep-alive>
</transition>
</router-view>
</template>
<script setup lang='ts'>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const key = computed(() => route.path)
</script>

13
dashboard/src/main.ts

@ -1,13 +0,0 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from '/@/router/index'
import { pinia } from '/@/store'
import '/@/permission'
import '/@/assets/css/index.css'
import 'virtual:svg-icons-register'
const app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')

59
dashboard/src/router/index.ts

@ -1,59 +0,0 @@
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { IMenubarList } from '../type/store/layout';
import { components } from './asyncRouter';
const Components: IObject<() => Promise<typeof import('*.vue')>> = Object.assign({}, components, {
LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise<typeof import('*.vue')>,
LayoutMain: (() => import('/@/layout/aside.vue')) as unknown as () => Promise<typeof import('*.vue')>
})
export const allowRouter: Array<IMenubarList> = [
{
name: 'Dashboard',
path: '/',
redirect: '/dashboard/nodes/living',
component: Components['LayoutHeader'],
meta: { title: 'Dashboard', activeMenu: '/dashboard' },
children: [
{
name: 'Nodes',
path: '/dashboard/nodes',
component: Components['LayoutMain'],
meta: { title: 'Nodes' },
children: [
{
name: 'LivingNodes',
path: '/dashboard/nodes/living',
component: Components['LivingNodes'],
meta: { title: 'Living Nodes' },
}
]
}, {
name: 'GlobalTransactions',
path: '/dashboard/global-transactions',
component: Components['LayoutMain'],
meta: { title: 'Global Transactions' },
children: [
{
name: 'AllTransactions',
path: '/dashboard/global-transactions/all',
component: Components['AllTransactions'],
meta: { title: 'All Transactions' },
}, {
name: 'UnfinishedTransactions',
path: '/dashboard/global-transactions/unfinished',
component: Components['UnfinishedTransactions'],
meta: { title: 'Unfinished Transactions' },
}
]
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes: allowRouter as RouteRecordRaw[]
})
export default router

20
dtmcli/barrier.go

@ -20,11 +20,13 @@ type BarrierBusiFunc func(tx *sql.Tx) error
// BranchBarrier every branch info // BranchBarrier every branch info
type BranchBarrier struct { type BranchBarrier struct {
TransType string TransType string
Gid string Gid string
BranchID string BranchID string
Op string Op string
BarrierID int BarrierID int
DBType string // DBTypeMysql | DBTypePostgres
BarrierTableName string
} }
func (bb *BranchBarrier) String() string { func (bb *BranchBarrier) String() string {
@ -70,8 +72,8 @@ func (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) (rerr error)
dtmimp.OpCompensate: dtmimp.OpAction, dtmimp.OpCompensate: dtmimp.OpAction,
}[bb.Op] }[bb.Op]
originAffected, oerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, originOp, bid, bb.Op) originAffected, oerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, originOp, bid, bb.Op, bb.DBType, bb.BarrierTableName)
currentAffected, rerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, bb.Op, bid, bb.Op) currentAffected, rerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, bb.Op, bid, bb.Op, bb.DBType, bb.BarrierTableName)
logger.Debugf("originAffected: %d currentAffected: %d", originAffected, currentAffected) logger.Debugf("originAffected: %d currentAffected: %d", originAffected, currentAffected)
if rerr == nil && bb.Op == dtmimp.MsgDoOp && currentAffected == 0 { // for msg's DoAndSubmit, repeated insert should be rejected. if rerr == nil && bb.Op == dtmimp.MsgDoOp && currentAffected == 0 { // for msg's DoAndSubmit, repeated insert should be rejected.
@ -103,10 +105,12 @@ func (bb *BranchBarrier) CallWithDB(db *sql.DB, busiCall BarrierBusiFunc) error
// QueryPrepared queries prepared data // QueryPrepared queries prepared data
func (bb *BranchBarrier) QueryPrepared(db *sql.DB) error { func (bb *BranchBarrier) QueryPrepared(db *sql.DB) error {
_, err := dtmimp.InsertBarrier(db, bb.TransType, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1, dtmimp.OpRollback) _, err := dtmimp.InsertBarrier(db, bb.TransType, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1, dtmimp.OpRollback, bb.DBType, bb.BarrierTableName)
var reason string var reason string
if err == nil { if err == nil {
sql := fmt.Sprintf("select reason from %s where gid=? and branch_id=? and op=? and barrier_id=?", dtmimp.BarrierTableName) sql := fmt.Sprintf("select reason from %s where gid=? and branch_id=? and op=? and barrier_id=?", dtmimp.BarrierTableName)
sql = dtmimp.GetDBSpecial(bb.DBType).GetPlaceHoldSQL(sql)
logger.Debugf("queryrow: %s", sql, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1)
err = db.QueryRow(sql, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1).Scan(&reason) err = db.QueryRow(sql, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1).Scan(&reason)
} }
if reason == dtmimp.OpRollback { if reason == dtmimp.OpRollback {

7
dtmcli/dtmimp/db_special.go

@ -75,8 +75,11 @@ func init() {
} }
// GetDBSpecial get DBSpecial for currentDBType // GetDBSpecial get DBSpecial for currentDBType
func GetDBSpecial() DBSpecial { func GetDBSpecial(dbType string) DBSpecial {
return dbSpecials[currentDBType] if dbType == "" {
dbType = currentDBType
}
return dbSpecials[dbType]
} }
// SetCurrentDBType set currentDBType // SetCurrentDBType set currentDBType

4
dtmcli/dtmimp/db_special_test.go

@ -18,13 +18,13 @@ func TestDBSpecial(t *testing.T) {
SetCurrentDBType("no-driver") SetCurrentDBType("no-driver")
})) }))
SetCurrentDBType(DBTypeMysql) SetCurrentDBType(DBTypeMysql)
sp := GetDBSpecial() sp := GetDBSpecial(DBTypeMysql)
assert.Equal(t, "? ?", sp.GetPlaceHoldSQL("? ?")) assert.Equal(t, "? ?", sp.GetPlaceHoldSQL("? ?"))
assert.Equal(t, "xa start 'xa1'", sp.GetXaSQL("start", "xa1")) assert.Equal(t, "xa start 'xa1'", sp.GetXaSQL("start", "xa1"))
assert.Equal(t, "insert ignore into a(f) values(?)", sp.GetInsertIgnoreTemplate("a(f) values(?)", "c")) assert.Equal(t, "insert ignore into a(f) values(?)", sp.GetInsertIgnoreTemplate("a(f) values(?)", "c"))
SetCurrentDBType(DBTypePostgres) SetCurrentDBType(DBTypePostgres)
sp = GetDBSpecial() sp = GetDBSpecial(DBTypePostgres)
assert.Equal(t, "$1 $2", sp.GetPlaceHoldSQL("? ?")) assert.Equal(t, "$1 $2", sp.GetPlaceHoldSQL("? ?"))
assert.Equal(t, "begin", sp.GetXaSQL("start", "xa1")) assert.Equal(t, "begin", sp.GetXaSQL("start", "xa1"))
assert.Equal(t, "insert into a(f) values(?) on conflict ON CONSTRAINT c do nothing", sp.GetInsertIgnoreTemplate("a(f) values(?)", "c")) assert.Equal(t, "insert into a(f) values(?) on conflict ON CONSTRAINT c do nothing", sp.GetInsertIgnoreTemplate("a(f) values(?)", "c"))

12
dtmcli/dtmimp/trans_xa_base.go

@ -18,14 +18,14 @@ func XaHandlePhase2(gid string, dbConf DBConf, branchID string, op string) error
return err return err
} }
xaID := gid + "-" + branchID xaID := gid + "-" + branchID
_, err = DBExec(db, GetDBSpecial().GetXaSQL(op, xaID)) _, err = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(op, xaID))
if err != nil && if err != nil &&
(strings.Contains(err.Error(), "XAER_NOTA") || strings.Contains(err.Error(), "does not exist")) { // Repeat commit/rollback with the same id, report this error, ignore (strings.Contains(err.Error(), "XAER_NOTA") || strings.Contains(err.Error(), "does not exist")) { // Repeat commit/rollback with the same id, report this error, ignore
err = nil err = nil
} }
if op == OpRollback && err == nil { if op == OpRollback && err == nil {
// rollback insert a row after prepare. no-error means prepare has finished. // rollback insert a row after prepare. no-error means prepare has finished.
_, err = InsertBarrier(db, "xa", gid, branchID, OpAction, XaBarrier1, op) _, err = InsertBarrier(db, "xa", gid, branchID, OpAction, XaBarrier1, op, dbConf.Driver, "")
} }
return err return err
} }
@ -39,20 +39,20 @@ func XaHandleLocalTrans(xa *TransBase, dbConf DBConf, cb func(*sql.DB) error) (r
} }
defer func() { _ = db.Close() }() defer func() { _ = db.Close() }()
defer DeferDo(&rerr, func() error { defer DeferDo(&rerr, func() error {
_, err := DBExec(db, GetDBSpecial().GetXaSQL("prepare", xaBranch)) _, err := DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL("prepare", xaBranch))
return err return err
}, func() error { }, func() error {
return nil return nil
}) })
_, rerr = DBExec(db, GetDBSpecial().GetXaSQL("start", xaBranch)) _, rerr = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL("start", xaBranch))
if rerr != nil { if rerr != nil {
return return
} }
defer func() { defer func() {
_, _ = DBExec(db, GetDBSpecial().GetXaSQL("end", xaBranch)) _, _ = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL("end", xaBranch))
}() }()
// prepare and rollback both insert a row // prepare and rollback both insert a row
_, rerr = InsertBarrier(db, xa.TransType, xa.Gid, xa.BranchID, OpAction, XaBarrier1, OpAction) _, rerr = InsertBarrier(db, xa.TransType, xa.Gid, xa.BranchID, OpAction, XaBarrier1, OpAction, dbConf.Driver, "")
if rerr == nil { if rerr == nil {
rerr = cb(db) rerr = cb(db)
} }

16
dtmcli/dtmimp/utils.go

@ -187,12 +187,12 @@ func XaDB(conf DBConf) (*sql.DB, error) {
} }
// DBExec use raw db to exec // DBExec use raw db to exec
func DBExec(db DB, sql string, values ...interface{}) (affected int64, rerr error) { func DBExec(dbType string, db DB, sql string, values ...interface{}) (affected int64, rerr error) {
if sql == "" { if sql == "" {
return 0, nil return 0, nil
} }
began := time.Now() began := time.Now()
sql = GetDBSpecial().GetPlaceHoldSQL(sql) sql = GetDBSpecial(dbType).GetPlaceHoldSQL(sql)
r, rerr := db.Exec(sql, values...) r, rerr := db.Exec(sql, values...)
used := time.Since(began) / time.Millisecond used := time.Since(began) / time.Millisecond
if rerr == nil { if rerr == nil {
@ -262,10 +262,16 @@ func EscapeGet(qs url.Values, key string) string {
} }
// InsertBarrier insert a record to barrier // InsertBarrier insert a record to barrier
func InsertBarrier(tx DB, transType string, gid string, branchID string, op string, barrierID string, reason string) (int64, error) { func InsertBarrier(tx DB, transType string, gid string, branchID string, op string, barrierID string, reason string, dbType string, barrierTableName string) (int64, error) {
if op == "" { if op == "" {
return 0, nil return 0, nil
} }
sql := GetDBSpecial().GetInsertIgnoreTemplate(BarrierTableName+"(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)", "uniq_barrier") if dbType == "" {
return DBExec(tx, sql, transType, gid, branchID, op, barrierID, reason) dbType = currentDBType
}
if barrierTableName == "" {
barrierTableName = BarrierTableName
}
sql := GetDBSpecial(dbType).GetInsertIgnoreTemplate(barrierTableName+"(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)", "uniq_barrier")
return DBExec(dbType, tx, sql, transType, gid, branchID, op, barrierID, reason)
} }

4
dtmgrpc/type.go

@ -8,6 +8,7 @@ package dtmgrpc
import ( import (
context "context" context "context"
"fmt"
"github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp" "github.com/dtm-labs/dtm/dtmcli/dtmimp"
@ -38,7 +39,8 @@ func GrpcError2DtmError(err error) error {
if st.Message() == dtmcli.ResultOngoing { if st.Message() == dtmcli.ResultOngoing {
return dtmcli.ErrOngoing return dtmcli.ErrOngoing
} }
return dtmcli.ErrFailure
return fmt.Errorf("%s. %w", st.Message(), dtmcli.ErrFailure)
} else if ok && st.Code() == codes.FailedPrecondition { } else if ok && st.Code() == codes.FailedPrecondition {
return dtmcli.ErrOngoing return dtmcli.ErrOngoing
} }

3
dtmsvr/api.go

@ -15,6 +15,9 @@ import (
"github.com/dtm-labs/dtm/dtmsvr/storage" "github.com/dtm-labs/dtm/dtmsvr/storage"
) )
// Version store the passin version for dtm server
var Version = ""
func svcSubmit(t *TransGlobal) interface{} { func svcSubmit(t *TransGlobal) interface{} {
t.Status = dtmcli.StatusSubmitted t.Status = dtmcli.StatusSubmitted
branches, err := t.saveNew() branches, err := t.saveNew()

6
dtmsvr/api_http.go

@ -19,6 +19,9 @@ import (
) )
func addRoute(engine *gin.Engine) { func addRoute(engine *gin.Engine) {
engine.GET("/api/dtmsvr/version", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return gin.H{"version": Version}
}))
engine.GET("/api/dtmsvr/newGid", dtmutil.WrapHandler2(newGid)) engine.GET("/api/dtmsvr/newGid", dtmutil.WrapHandler2(newGid))
engine.POST("/api/dtmsvr/prepare", dtmutil.WrapHandler2(prepare)) engine.POST("/api/dtmsvr/prepare", dtmutil.WrapHandler2(prepare))
engine.POST("/api/dtmsvr/submit", dtmutil.WrapHandler2(submit)) engine.POST("/api/dtmsvr/submit", dtmutil.WrapHandler2(submit))
@ -89,8 +92,7 @@ func all(c *gin.Context) interface{} {
return map[string]interface{}{"transactions": globals, "next_position": position} return map[string]interface{}{"transactions": globals, "next_position": position}
} }
// resetCronTime rest nextCronTime // unfinished transactions need to be retried as soon as possible after business downtime is recovered
// Prevent multiple backoff from causing NextCronTime to be too long
func resetCronTime(c *gin.Context) interface{} { func resetCronTime(c *gin.Context) interface{} {
sTimeoutSecond := dtmimp.OrString(c.Query("timeout"), strconv.FormatInt(3*conf.TimeoutToFail, 10)) sTimeoutSecond := dtmimp.OrString(c.Query("timeout"), strconv.FormatInt(3*conf.TimeoutToFail, 10))
sLimit := dtmimp.OrString(c.Query("limit"), "100") sLimit := dtmimp.OrString(c.Query("limit"), "100")

13
dtmsvr/config/config.go

@ -6,7 +6,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/logger" "github.com/dtm-labs/dtm/dtmcli/logger"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v3"
) )
const ( const (
@ -53,14 +53,13 @@ type Store struct {
Port int64 `yaml:"Port"` Port int64 `yaml:"Port"`
User string `yaml:"User"` User string `yaml:"User"`
Password string `yaml:"Password"` Password string `yaml:"Password"`
Db string `yaml:"Db" default:"dtm"`
MaxOpenConns int64 `yaml:"MaxOpenConns" default:"500"` MaxOpenConns int64 `yaml:"MaxOpenConns" default:"500"`
MaxIdleConns int64 `yaml:"MaxIdleConns" default:"500"` MaxIdleConns int64 `yaml:"MaxIdleConns" default:"500"`
ConnMaxLifeTime int64 `yaml:"ConnMaxLifeTime" default:"5"` ConnMaxLifeTime int64 `yaml:"ConnMaxLifeTime" default:"5"`
DataExpire int64 `yaml:"DataExpire" default:"604800"` // Trans data will expire in 7 days. only for redis/boltdb. DataExpire int64 `yaml:"DataExpire" default:"604800"` // Trans data will expire in 7 days. only for redis/boltdb.
FinishedDataExpire int64 `yaml:"FinishedDataExpire" default:"86400"` // finished Trans data will expire in 1 days. only for redis. FinishedDataExpire int64 `yaml:"FinishedDataExpire" default:"86400"` // finished Trans data will expire in 1 days. only for redis.
RedisPrefix string `yaml:"RedisPrefix" default:"{a}"` // Redis storage prefix. store data to only one slot in cluster RedisPrefix string `yaml:"RedisPrefix" default:"{a}"` // Redis storage prefix. store data to only one slot in cluster
TransGlobalTable string `yaml:"TransGlobalTable" default:"dtm.trans_global"`
TransBranchOpTable string `yaml:"TransBranchOpTable" default:"dtm.trans_branch_op"`
} }
// IsDB checks config driver is mysql or postgres // IsDB checks config driver is mysql or postgres
@ -76,10 +75,12 @@ func (s *Store) GetDBConf() dtmcli.DBConf {
Port: s.Port, Port: s.Port,
User: s.User, User: s.User,
Password: s.Password, Password: s.Password,
Db: s.Db,
} }
} }
type configType struct { // Type is the type for the config of dtm server
type Type struct {
Store Store `yaml:"Store"` Store Store `yaml:"Store"`
TransCronInterval int64 `yaml:"TransCronInterval" default:"3"` TransCronInterval int64 `yaml:"TransCronInterval" default:"3"`
TimeoutToFail int64 `yaml:"TimeoutToFail" default:"35"` TimeoutToFail int64 `yaml:"TimeoutToFail" default:"35"`
@ -97,7 +98,7 @@ type configType struct {
} }
// Config config // Config config
var Config = configType{} var Config = Type{}
// MustLoadConfig load config from env and file // MustLoadConfig load config from env and file
func MustLoadConfig(confFile string) { func MustLoadConfig(confFile string) {
@ -105,7 +106,7 @@ func MustLoadConfig(confFile string) {
if confFile != "" { if confFile != "" {
cont, err := ioutil.ReadFile(confFile) cont, err := ioutil.ReadFile(confFile)
logger.FatalIfError(err) logger.FatalIfError(err)
err = yaml.UnmarshalStrict(cont, &Config) err = yaml.Unmarshal(cont, &Config)
logger.FatalIfError(err) logger.FatalIfError(err)
} }
scont, err := json.MarshalIndent(&Config, "", " ") scont, err := json.MarshalIndent(&Config, "", " ")

2
dtmsvr/config/config_utils.go

@ -58,7 +58,7 @@ func toUnderscoreUpper(key string) string {
return strings.ToUpper(s2) return strings.ToUpper(s2)
} }
func checkConfig(conf *configType) error { func checkConfig(conf *Type) error {
if conf.RetryInterval < 10 { if conf.RetryInterval < 10 {
return errors.New("RetryInterval should not be less than 10") return errors.New("RetryInterval should not be less than 10")
} }

21
dtmsvr/entry/main.go

@ -14,13 +14,6 @@ import (
"go.uber.org/automaxprocs/maxprocs" "go.uber.org/automaxprocs/maxprocs"
) )
func ver(version *string) {
if *version == "" {
*version = "0.0.0-dev"
}
fmt.Printf("dtm version: %s\n", *version)
}
func usage() { func usage() {
cmd := filepath.Base(os.Args[0]) cmd := filepath.Base(os.Args[0])
s := "Usage: %s [options]\n\n" s := "Usage: %s [options]\n\n"
@ -35,14 +28,18 @@ var isReset = flag.Bool("r", false, "Reset dtm server data.")
var confFile = flag.String("c", "", "Path to the server configuration file.") var confFile = flag.String("c", "", "Path to the server configuration file.")
// Main is the entry point of dtm server. // Main is the entry point of dtm server.
func Main(version *string) *gin.Engine { func Main(version *string) (*gin.Engine, *config.Type) {
flag.Parse() flag.Parse()
if *version == "" {
*version = "v0.0.0-dev"
}
dtmsvr.Version = *version
if flag.NArg() > 0 || *isHelp { if flag.NArg() > 0 || *isHelp {
usage() usage()
return nil return nil, nil
} else if *isVersion { } else if *isVersion {
ver(version) fmt.Printf("dtm version: %s\n", *version)
return nil return nil, nil
} }
logger.Infof("dtm version is: %s", *version) logger.Infof("dtm version is: %s", *version)
config.MustLoadConfig(*confFile) config.MustLoadConfig(*confFile)
@ -58,5 +55,5 @@ func Main(version *string) *gin.Engine {
registry.WaitStoreUp() registry.WaitStoreUp()
app := dtmsvr.StartSvr() // start dtmsvr api app := dtmsvr.StartSvr() // start dtmsvr api
go dtmsvr.CronExpiredTrans(-1) // start dtmsvr cron job go dtmsvr.CronExpiredTrans(-1) // start dtmsvr cron job
return app return app, &config.Config
} }

41
dtmsvr/storage/boltdb/boltdb.go

@ -11,7 +11,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp" "github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmcli/logger" "github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/dtm-labs/dtm/dtmsvr/storage" "github.com/dtm-labs/dtm/dtmsvr/storage"
@ -276,7 +275,7 @@ func (s *Store) ScanTransGlobalStores(position *string, limit int64) []storage.T
globals := []storage.TransGlobalStore{} globals := []storage.TransGlobalStore{}
err := s.boltDb.View(func(t *bolt.Tx) error { err := s.boltDb.View(func(t *bolt.Tx) error {
cursor := t.Bucket(bucketGlobal).Cursor() cursor := t.Bucket(bucketGlobal).Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() { for k, v := cursor.Seek([]byte(*position)); k != nil; k, v = cursor.Next() {
if string(k) == *position { if string(k) == *position {
continue continue
} }
@ -383,42 +382,42 @@ func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval
// LockOneGlobalTrans finds GlobalTrans // LockOneGlobalTrans finds GlobalTrans
func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore { func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
var trans *storage.TransGlobalStore var transo *storage.TransGlobalStore
min := fmt.Sprintf("%d", time.Now().Add(expireIn).Unix()) min := fmt.Sprintf("%d", time.Now().Add(expireIn).Unix())
next := time.Now().Add(time.Duration(s.retryInterval) * time.Second)
err := s.boltDb.Update(func(t *bolt.Tx) error { err := s.boltDb.Update(func(t *bolt.Tx) error {
cursor := t.Bucket(bucketIndex).Cursor() cursor := t.Bucket(bucketIndex).Cursor()
toDelete := [][]byte{} toDelete := [][]byte{}
for trans == nil || trans.Status == dtmcli.StatusSucceed || trans.Status == dtmcli.StatusFailed { for k, v := cursor.First(); k != nil && string(k) <= min; k, v = cursor.Next() {
k, v := cursor.First()
if k == nil || string(k) > min {
return storage.ErrNotFound
}
trans = tGetGlobal(t, string(v))
toDelete = append(toDelete, k) toDelete = append(toDelete, k)
trans := tGetGlobal(t, string(v))
if trans != nil && !trans.IsFinished() {
transo = trans
break
}
} }
for _, k := range toDelete { for _, k := range toDelete {
err := t.Bucket(bucketIndex).Delete(k) err := t.Bucket(bucketIndex).Delete(k)
dtmimp.E2P(err) dtmimp.E2P(err)
} }
trans.NextCronTime = &next if transo != nil {
tPutGlobal(t, trans) next := time.Now().Add(time.Duration(s.retryInterval) * time.Second)
tPutIndex(t, next.Unix(), trans.Gid) transo.NextCronTime = &next
tPutGlobal(t, transo)
tPutIndex(t, next.Unix(), transo.Gid)
}
return nil return nil
}) })
if err == storage.ErrNotFound {
return nil
}
dtmimp.E2P(err) dtmimp.E2P(err)
return trans return transo
} }
// ResetCronTime rest nextCronTime // ResetCronTime reset nextCronTime
// Prevent multiple backoff from causing NextCronTime to be too long // unfinished transactions need to be retried as soon as possible after business downtime is recovered
func (s *Store) ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) {
next := time.Now() next := time.Now()
var trans *storage.TransGlobalStore var trans *storage.TransGlobalStore
min := fmt.Sprintf("%d", time.Now().Add(timeout).Unix()) min := fmt.Sprintf("%d", time.Now().Add(after).Unix())
err = s.boltDb.Update(func(t *bolt.Tx) error { err = s.boltDb.Update(func(t *bolt.Tx) error {
cursor := t.Bucket(bucketIndex).Cursor() cursor := t.Bucket(bucketIndex).Cursor()
succeedCount = 0 succeedCount = 0

8
dtmsvr/storage/redis/redis.go

@ -270,11 +270,11 @@ return gid
} }
} }
// ResetCronTime rest nextCronTime // ResetCronTime reset nextCronTime
// Prevent multiple backoff from causing NextCronTime to be too long // unfinished transactions need to be retried as soon as possible after business downtime is recovered
func (s *Store) ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) {
next := time.Now().Unix() next := time.Now().Unix()
timeoutTimestamp := time.Now().Add(timeout).Unix() timeoutTimestamp := time.Now().Add(after).Unix()
args := newArgList().AppendGid("").AppendRaw(timeoutTimestamp).AppendRaw(next).AppendRaw(limit) args := newArgList().AppendGid("").AppendRaw(timeoutTimestamp).AppendRaw(next).AppendRaw(limit)
lua := `-- ResetCronTime lua := `-- ResetCronTime
local r = redis.call('ZRANGEBYSCORE', KEYS[3], ARGV[3], '+inf', 'LIMIT', 0, ARGV[5]+1) local r = redis.call('ZRANGEBYSCORE', KEYS[3], ARGV[3], '+inf', 'LIMIT', 0, ARGV[5]+1)

2
dtmsvr/storage/registry/registry.go

@ -49,7 +49,7 @@ func GetStore() storage.Store {
// WaitStoreUp wait for db to go up // WaitStoreUp wait for db to go up
func WaitStoreUp() { func WaitStoreUp() {
for err := GetStore().Ping(); err != nil; err = GetStore().Ping() { for err := GetStore().Ping(); err != nil; err = GetStore().Ping() {
logger.Infof("wait store up") logger.Infof("wait store up: %v", err)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
} }
} }

81
dtmsvr/storage/sql/sql.go

@ -137,61 +137,44 @@ func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval
// LockOneGlobalTrans finds GlobalTrans // LockOneGlobalTrans finds GlobalTrans
func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore { func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {
db := dbGet() db := dbGet()
getTime := func(second int) string {
return map[string]string{
"mysql": fmt.Sprintf("date_add(now(), interval %d second)", second),
"postgres": fmt.Sprintf("current_timestamp + interval '%d second'", second),
}[conf.Store.Driver]
}
expire := int(expireIn / time.Second)
whereTime := fmt.Sprintf("next_cron_time < %s", getTime(expire))
owner := shortuuid.New() owner := shortuuid.New()
global := &storage.TransGlobalStore{} nextCronTime := getTimeStr(int64(expireIn / time.Second))
dbr := db.Must().Model(global). where := map[string]string{
Where(whereTime + "and status in ('prepared', 'aborting', 'submitted')"). dtmimp.DBTypeMysql: fmt.Sprintf(`next_cron_time < '%s' and status in ('prepared', 'aborting', 'submitted') limit 1`, nextCronTime),
Limit(1). dtmimp.DBTypePostgres: fmt.Sprintf(`id in (select id from trans_global where next_cron_time < '%s' and status in ('prepared', 'aborting', 'submitted') limit 1 )`, nextCronTime),
Select([]string{"owner", "next_cron_time"}). }[conf.Store.Driver]
Updates(&storage.TransGlobalStore{
Owner: owner, sql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s', owner='%s' WHERE %s`,
NextCronTime: dtmutil.GetNextTime(conf.RetryInterval), getTimeStr(0),
}) getTimeStr(conf.RetryInterval),
if dbr.RowsAffected == 0 { owner,
where)
affected, err := dtmimp.DBExec(conf.Store.Driver, db.ToSQLDB(), sql)
dtmimp.PanicIf(err != nil, err)
if affected == 0 {
return nil return nil
} }
global := &storage.TransGlobalStore{}
db.Must().Where("owner=?", owner).First(global) db.Must().Where("owner=?", owner).First(global)
return global return global
} }
// ResetCronTime rest nextCronTime // ResetCronTime reset nextCronTime
// Prevent multiple backoff from causing NextCronTime to be too long // unfinished transactions need to be retried as soon as possible after business downtime is recovered
func (s *Store) ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) {
db := dbGet() nextCronTime := getTimeStr(int64(after / time.Second))
getTime := func(second int) string { where := map[string]string{
return map[string]string{ dtmimp.DBTypeMysql: fmt.Sprintf(`next_cron_time > '%s' and status in ('prepared', 'aborting', 'submitted') limit %d`, nextCronTime, limit),
"mysql": fmt.Sprintf("date_add(now(), interval %d second)", second), dtmimp.DBTypePostgres: fmt.Sprintf(`id in (select id from trans_global where next_cron_time > '%s' and status in ('prepared', 'aborting', 'submitted') limit %d )`, nextCronTime, limit),
"postgres": fmt.Sprintf("current_timestamp + interval '%d second'", second), }[conf.Store.Driver]
}[conf.Store.Driver]
}
timeoutSecond := int(timeout / time.Second)
whereTime := fmt.Sprintf("next_cron_time > %s", getTime(timeoutSecond))
global := &storage.TransGlobalStore{}
dbr := db.Must().Model(global).
Where(whereTime + "and status in ('prepared', 'aborting', 'submitted')").
Limit(int(limit)).
Select([]string{"next_cron_time"}).
Updates(&storage.TransGlobalStore{
NextCronTime: dtmutil.GetNextTime(0),
})
succeedCount = dbr.RowsAffected
if succeedCount == limit {
var count int64
db.Must().Model(global).Where(whereTime + "and status in ('prepared', 'aborting', 'submitted')").Limit(1).Count(&count)
if count > 0 {
hasRemaining = true
}
}
return succeedCount, hasRemaining, dbr.Error sql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s' WHERE %s`,
getTimeStr(0),
getTimeStr(0),
where)
affected, err := dtmimp.DBExec(conf.Store.Driver, dbGet().ToSQLDB(), sql)
return affected, affected == limit, err
} }
// SetDBConn sets db conn pool // SetDBConn sets db conn pool
@ -213,3 +196,7 @@ func wrapError(err error) error {
dtmimp.E2P(err) dtmimp.E2P(err)
return err return err
} }
func getTimeStr(afterSecond int64) string {
return dtmutil.GetNextTime(afterSecond).Format("2006-01-02 15:04:05")
}

2
dtmsvr/storage/store.go

@ -30,5 +30,5 @@ type Store interface {
ChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool) ChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool)
TouchCronTime(global *TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time) TouchCronTime(global *TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time)
LockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore LockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore
ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error)
} }

12
dtmsvr/storage/trans.go

@ -11,7 +11,6 @@ import (
"github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp" "github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmsvr/config"
"github.com/dtm-labs/dtm/dtmutil" "github.com/dtm-labs/dtm/dtmutil"
) )
@ -33,6 +32,7 @@ type TransGlobalStore struct {
Protocol string `json:"protocol,omitempty"` Protocol string `json:"protocol,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"` FinishTime *time.Time `json:"finish_time,omitempty"`
RollbackTime *time.Time `json:"rollback_time,omitempty"` RollbackTime *time.Time `json:"rollback_time,omitempty"`
RollbackReason string `json:"rollback_reason,omitempty"`
Options string `json:"options,omitempty"` Options string `json:"options,omitempty"`
CustomData string `json:"custom_data,omitempty"` CustomData string `json:"custom_data,omitempty"`
NextCronInterval int64 `json:"next_cron_interval,omitempty"` NextCronInterval int64 `json:"next_cron_interval,omitempty"`
@ -45,13 +45,18 @@ type TransGlobalStore struct {
// TableName TableName // TableName TableName
func (g *TransGlobalStore) TableName() string { func (g *TransGlobalStore) TableName() string {
return config.Config.Store.TransGlobalTable return "trans_global"
} }
func (g *TransGlobalStore) String() string { func (g *TransGlobalStore) String() string {
return dtmimp.MustMarshalString(g) return dtmimp.MustMarshalString(g)
} }
// IsFinished return true if status == "failed" || status == "succeed"
func (g *TransGlobalStore) IsFinished() bool {
return g.Status == dtmcli.StatusFailed || g.Status == dtmcli.StatusSucceed
}
// TransBranchStore branch transaction // TransBranchStore branch transaction
type TransBranchStore struct { type TransBranchStore struct {
dtmutil.ModelBase dtmutil.ModelBase
@ -63,11 +68,12 @@ type TransBranchStore struct {
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"` FinishTime *time.Time `json:"finish_time,omitempty"`
RollbackTime *time.Time `json:"rollback_time,omitempty"` RollbackTime *time.Time `json:"rollback_time,omitempty"`
Error error `json:"-" gorm:"-"`
} }
// TableName TableName // TableName TableName
func (b *TransBranchStore) TableName() string { func (b *TransBranchStore) TableName() string {
return config.Config.Store.TransBranchOpTable return "trans_branch_op"
} }
func (b *TransBranchStore) String() string { func (b *TransBranchStore) String() string {

22
dtmsvr/trans_status.go

@ -36,7 +36,22 @@ func (t *TransGlobal) touchCronTime(ctype cronType, delay uint64) {
logger.Infof("TouchCronTime for: %s", t.TransGlobalStore.String()) logger.Infof("TouchCronTime for: %s", t.TransGlobalStore.String())
} }
func (t *TransGlobal) changeStatus(status string) { type changeStatusParams struct {
rollbackReason string
}
type changeStatusOption func(c *changeStatusParams)
func withRollbackReason(rollbackReason string) changeStatusOption {
return func(c *changeStatusParams) {
c.rollbackReason = rollbackReason
}
}
func (t *TransGlobal) changeStatus(status string, opts ...changeStatusOption) {
statusParams := &changeStatusParams{}
for _, opt := range opts {
opt(statusParams)
}
updates := []string{"status", "update_time"} updates := []string{"status", "update_time"}
now := time.Now() now := time.Now()
if status == dtmcli.StatusSucceed { if status == dtmcli.StatusSucceed {
@ -46,6 +61,10 @@ func (t *TransGlobal) changeStatus(status string) {
t.RollbackTime = &now t.RollbackTime = &now
updates = append(updates, "rollback_time") updates = append(updates, "rollback_time")
} }
if statusParams.rollbackReason != "" {
t.RollbackReason = statusParams.rollbackReason
updates = append(updates, "rollback_reason")
}
t.UpdateTime = &now t.UpdateTime = &now
GetStore().ChangeGlobalStatus(&t.TransGlobalStore, status, updates, status == dtmcli.StatusSucceed || status == dtmcli.StatusFailed) GetStore().ChangeGlobalStatus(&t.TransGlobalStore, status, updates, status == dtmcli.StatusSucceed || status == dtmcli.StatusFailed)
logger.Infof("ChangeGlobalStatus to %s ok for %s", status, t.TransGlobalStore.String()) logger.Infof("ChangeGlobalStatus to %s ok for %s", status, t.TransGlobalStore.String())
@ -171,6 +190,7 @@ func (t *TransGlobal) getBranchResult(branch *TransBranch) (string, error) {
if err == nil { if err == nil {
return dtmcli.StatusSucceed, nil return dtmcli.StatusSucceed, nil
} else if t.TransType == "saga" && branch.Op == dtmimp.OpAction && errors.Is(err, dtmcli.ErrFailure) { } else if t.TransType == "saga" && branch.Op == dtmimp.OpAction && errors.Is(err, dtmcli.ErrFailure) {
branch.Error = fmt.Errorf("url:%s return failed: %w", branch.URL, err)
return dtmcli.StatusFailed, nil return dtmcli.StatusFailed, nil
} else if errors.Is(err, dtmcli.ErrOngoing) { } else if errors.Is(err, dtmcli.ErrOngoing) {
return "", dtmcli.ErrOngoing return "", dtmcli.ErrOngoing

16
dtmsvr/trans_type_saga.go

@ -49,13 +49,14 @@ type branchResult struct {
status string status string
started bool started bool
op string op string
err error
} }
func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error { func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
// when saga tasks is fetched, it always need to process // when saga tasks is fetched, it always need to process
logger.Debugf("status: %s timeout: %t", t.Status, t.isTimeout()) logger.Debugf("status: %s timeout: %t", t.Status, t.isTimeout())
if t.Status == dtmcli.StatusSubmitted && t.isTimeout() { if t.Status == dtmcli.StatusSubmitted && t.isTimeout() {
t.changeStatus(dtmcli.StatusAborting) t.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf("Timeout after %d seconds", t.TimeoutToFail)))
} }
n := len(branches) n := len(branches)
@ -73,6 +74,7 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
} }
// resultStats // resultStats
var rsAToStart, rsAStarted, rsADone, rsAFailed, rsASucceed, rsCToStart, rsCDone, rsCSucceed int var rsAToStart, rsAStarted, rsADone, rsAFailed, rsASucceed, rsCToStart, rsCDone, rsCSucceed int
var failureError error
branchResults := make([]branchResult, n) // save the branch result branchResults := make([]branchResult, n) // save the branch result
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
b := branches[i] b := branches[i]
@ -125,9 +127,9 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
if x := recover(); x != nil { if x := recover(); x != nil {
err = dtmimp.AsError(x) err = dtmimp.AsError(x)
} }
resultChan <- branchResult{index: i, status: branches[i].Status, op: branches[i].Op} resultChan <- branchResult{index: i, status: branches[i].Status, op: branches[i].Op, err: branches[i].Error}
if err != nil && !errors.Is(err, dtmcli.ErrOngoing) { if err != nil && !errors.Is(err, dtmcli.ErrOngoing) {
logger.Errorf("exec branch error: %v", err) logger.Errorf("exec branch %s %s %s error: %v", branches[i].BranchID, branches[i].Op, branches[i].URL, err)
} }
}() }()
err = t.execBranch(&branches[i], i) err = t.execBranch(&branches[i], i)
@ -172,6 +174,7 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
rsADone++ rsADone++
if r.status == dtmcli.StatusFailed { if r.status == dtmcli.StatusFailed {
rsAFailed++ rsAFailed++
failureError = r.err
} else if r.status == dtmcli.StatusSucceed { } else if r.status == dtmcli.StatusSucceed {
rsASucceed++ rsASucceed++
} }
@ -219,8 +222,11 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error {
t.changeStatus(dtmcli.StatusSucceed) t.changeStatus(dtmcli.StatusSucceed)
return nil return nil
} }
if t.Status == dtmcli.StatusSubmitted && (rsAFailed > 0 || t.isTimeout()) { if t.Status == dtmcli.StatusSubmitted && rsAFailed > 0 {
t.changeStatus(dtmcli.StatusAborting) t.changeStatus(dtmcli.StatusAborting, withRollbackReason(failureError.Error()))
}
if t.Status == dtmcli.StatusSubmitted && t.isTimeout() {
t.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf("Timeout after %d seconds", t.TimeoutToFail)))
} }
if t.Status == dtmcli.StatusAborting { if t.Status == dtmcli.StatusAborting {
prepareToCompensate() prepareToCompensate()

4
dtmsvr/trans_type_tcc.go

@ -1,6 +1,8 @@
package dtmsvr package dtmsvr
import ( import (
"fmt"
"github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp" "github.com/dtm-labs/dtm/dtmcli/dtmimp"
"github.com/dtm-labs/dtm/dtmcli/logger" "github.com/dtm-labs/dtm/dtmcli/logger"
@ -23,7 +25,7 @@ func (t *transTccProcessor) ProcessOnce(branches []TransBranch) error {
return nil return nil
} }
if t.Status == dtmcli.StatusPrepared && t.isTimeout() { if t.Status == dtmcli.StatusPrepared && t.isTimeout() {
t.changeStatus(dtmcli.StatusAborting) t.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf("Timeout after %d seconds", t.TimeoutToFail)))
} }
op := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmimp.OpConfirm, dtmimp.OpCancel).(string) op := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmimp.OpConfirm, dtmimp.OpCancel).(string)
for current := len(branches) - 1; current >= 0; current-- { for current := len(branches) - 1; current >= 0; current-- {

4
dtmsvr/trans_type_xa.go

@ -1,6 +1,8 @@
package dtmsvr package dtmsvr
import ( import (
"fmt"
"github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli"
"github.com/dtm-labs/dtm/dtmcli/dtmimp" "github.com/dtm-labs/dtm/dtmcli/dtmimp"
) )
@ -22,7 +24,7 @@ func (t *transXaProcessor) ProcessOnce(branches []TransBranch) error {
return nil return nil
} }
if t.Status == dtmcli.StatusPrepared && t.isTimeout() { if t.Status == dtmcli.StatusPrepared && t.isTimeout() {
t.changeStatus(dtmcli.StatusAborting) t.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf("Timeout after %d seconds", t.TimeoutToFail)))
} }
currentType := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmimp.OpCommit, dtmimp.OpRollback).(string) currentType := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmimp.OpCommit, dtmimp.OpRollback).(string)
for i, branch := range branches { for i, branch := range branches {

2
dtmutil/db.go

@ -100,7 +100,7 @@ func DbGet(conf dtmcli.DBConf, ops ...func(*gorm.DB)) *DB {
dsn := dtmimp.GetDsn(conf) dsn := dtmimp.GetDsn(conf)
db, ok := dbs.Load(dsn) db, ok := dbs.Load(dsn)
if !ok { if !ok {
logger.Infof("connecting '%s' '%s' '%s' '%d'", conf.Driver, conf.Host, conf.User, conf.Port) logger.Infof("connecting '%s' '%s' '%s' '%d' '%s'", conf.Driver, conf.Host, conf.User, conf.Port, conf.Db)
db1, err := gorm.Open(getGormDialetor(conf.Driver, dsn), &gorm.Config{ db1, err := gorm.Open(getGormDialetor(conf.Driver, dsn), &gorm.Config{
SkipDefaultTransaction: true, SkipDefaultTransaction: true,
}) })

4
dtmutil/utils.go

@ -64,7 +64,7 @@ func WrapHandler(fn func(*gin.Context) interface{}) gin.HandlerFunc {
} }
} }
// WrapHandler2 wrap a function te bo the handler of gin request // WrapHandler2 wrap a function to be the handler of gin request
// used by dtmsvr // used by dtmsvr
func WrapHandler2(fn func(*gin.Context) interface{}) gin.HandlerFunc { func WrapHandler2(fn func(*gin.Context) interface{}) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
@ -168,7 +168,7 @@ func RunSQLScript(conf dtmcli.DBConf, script string, skipDrop bool) {
if s == "" || (skipDrop && strings.Contains(s, "drop")) { if s == "" || (skipDrop && strings.Contains(s, "drop")) {
continue continue
} }
_, err = dtmimp.DBExec(con, s) _, err = dtmimp.DBExec(conf.Driver, con, s)
logger.FatalIfError(err) logger.FatalIfError(err)
logger.Infof("sql scripts finished: %s", s) logger.Infof("sql scripts finished: %s", s)
} }

13
go.mod

@ -6,12 +6,13 @@ require (
bou.ke/monkey v1.0.2 bou.ke/monkey v1.0.2
github.com/BurntSushi/toml v0.4.1 // indirect github.com/BurntSushi/toml v0.4.1 // indirect
github.com/dtm-labs/dtmdriver v0.0.3 github.com/dtm-labs/dtmdriver v0.0.3
github.com/dtm-labs/dtmdriver-gozero v0.0.4 github.com/dtm-labs/dtmdriver-gozero v0.0.5
github.com/dtm-labs/dtmdriver-http v1.2.2 github.com/dtm-labs/dtmdriver-http v1.2.2
github.com/dtm-labs/dtmdriver-kratos v0.0.8 github.com/dtm-labs/dtmdriver-kratos v0.0.8
github.com/dtm-labs/dtmdriver-polaris v0.0.4 github.com/dtm-labs/dtmdriver-polaris v0.0.4
github.com/dtm-labs/dtmdriver-protocol1 v0.0.1 github.com/dtm-labs/dtmdriver-protocol1 v0.0.1
github.com/gin-gonic/gin v1.7.7 github.com/gin-gonic/gin v1.7.7
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/go-redis/redis/v8 v8.11.4 github.com/go-redis/redis/v8 v8.11.4
github.com/go-resty/resty/v2 v2.7.0 github.com/go-resty/resty/v2 v2.7.0
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
@ -22,14 +23,16 @@ require (
github.com/onsi/gomega v1.16.0 github.com/onsi/gomega v1.16.0
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.1 github.com/stretchr/testify v1.7.1
github.com/ugorji/go v1.2.7 // indirect
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
go.mongodb.org/mongo-driver v1.8.3 go.mongodb.org/mongo-driver v1.9.0
go.uber.org/automaxprocs v1.5.1 go.uber.org/automaxprocs v1.5.1
go.uber.org/zap v1.21.0 go.uber.org/zap v1.21.0
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
google.golang.org/grpc v1.45.0 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
google.golang.org/grpc v1.46.2
google.golang.org/protobuf v1.28.0 google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.0.3 gorm.io/driver/mysql v1.0.3
gorm.io/driver/postgres v1.2.1 gorm.io/driver/postgres v1.2.1
gorm.io/gorm v1.22.2 gorm.io/gorm v1.22.2

100
go.sum

@ -67,8 +67,10 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.17.0/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I= github.com/alicebob/miniredis/v2 v2.17.0/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1402/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1620 h1:diBUSNskvZjiX1LFa3I+AfHaVnKvphXHddf0eVuCVFg=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1620/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -85,6 +87,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -106,6 +109,7 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
@ -131,10 +135,8 @@ github.com/dtm-labs/dtmdriver v0.0.1/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTx
github.com/dtm-labs/dtmdriver v0.0.2/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTxa/PP0jmFuk= github.com/dtm-labs/dtmdriver v0.0.2/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTxa/PP0jmFuk=
github.com/dtm-labs/dtmdriver v0.0.3 h1:9iAtvXKR3lJXQ7dvS87e4xdtmqkzN+ofek+CF9AvUSY= github.com/dtm-labs/dtmdriver v0.0.3 h1:9iAtvXKR3lJXQ7dvS87e4xdtmqkzN+ofek+CF9AvUSY=
github.com/dtm-labs/dtmdriver v0.0.3/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTxa/PP0jmFuk= github.com/dtm-labs/dtmdriver v0.0.3/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTxa/PP0jmFuk=
github.com/dtm-labs/dtmdriver-gozero v0.0.3 h1:KXaEFYzykSEhiLB2+baV8X1PaHLKh85bpJISK8+H86E= github.com/dtm-labs/dtmdriver-gozero v0.0.5 h1:cMBkURDRPcb1Y6/kY+x/yxYA8KWMG+fkK6b+cs4WXKo=
github.com/dtm-labs/dtmdriver-gozero v0.0.3/go.mod h1:B5FqzfvzGe2AcB8MpAP34eSLszkCd6hm+t5KJN2Fu/o= github.com/dtm-labs/dtmdriver-gozero v0.0.5/go.mod h1:U4nIisQ5SBZrajp7ky1Bi0eBxPiGqVy+tOs47beobY4=
github.com/dtm-labs/dtmdriver-gozero v0.0.4 h1:GN+x3pmk545BgvbR4oiKhJ7N8k5yZ2rkG1aCwj8ZDZ8=
github.com/dtm-labs/dtmdriver-gozero v0.0.4/go.mod h1:B5FqzfvzGe2AcB8MpAP34eSLszkCd6hm+t5KJN2Fu/o=
github.com/dtm-labs/dtmdriver-http v1.2.2 h1:QOul+PpK1KQyXXx5viNrHrEFIc/nFxmX4fJfI3DLUqI= github.com/dtm-labs/dtmdriver-http v1.2.2 h1:QOul+PpK1KQyXXx5viNrHrEFIc/nFxmX4fJfI3DLUqI=
github.com/dtm-labs/dtmdriver-http v1.2.2/go.mod h1:UtWShS61TiiudZUAabQ2ww0CzSEpBYF3AS3F3G2Jc2o= github.com/dtm-labs/dtmdriver-http v1.2.2/go.mod h1:UtWShS61TiiudZUAabQ2ww0CzSEpBYF3AS3F3G2Jc2o=
github.com/dtm-labs/dtmdriver-kratos v0.0.8 h1:AAIfFzpzuu7K3B/wpx71y0FdZqO1X/Bsklva0bayS9s= github.com/dtm-labs/dtmdriver-kratos v0.0.8 h1:AAIfFzpzuu7K3B/wpx71y0FdZqO1X/Bsklva0bayS9s=
@ -156,8 +158,10 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
@ -178,8 +182,10 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.4.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -218,12 +224,15 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
@ -444,14 +453,19 @@ github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/U
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -487,8 +501,12 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8=
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -551,6 +569,7 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nacos-group/nacos-sdk-go v1.0.9/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=
github.com/nacos-group/nacos-sdk-go v1.1.1 h1:beczWcOoTaVBMgCgikqvZflrN5Xbw7pWAWpxl+VJGIA= github.com/nacos-group/nacos-sdk-go v1.1.1 h1:beczWcOoTaVBMgCgikqvZflrN5Xbw7pWAWpxl+VJGIA=
github.com/nacos-group/nacos-sdk-go v1.1.1/go.mod h1:UHOtQNQY/qpk2dhg6gDq8u5+/CEIc3+lWmrmxEzX0/g= github.com/nacos-group/nacos-sdk-go v1.1.1/go.mod h1:UHOtQNQY/qpk2dhg6gDq8u5+/CEIc3+lWmrmxEzX0/g=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
@ -626,6 +645,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
@ -673,15 +693,19 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@ -700,10 +724,13 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/zeromicro/go-zero v1.3.0/go.mod h1:Hy4o1VFAt32lXaQMbaBhoFeZjA/rJqJ4PTGNdGsURcc= github.com/zeromicro/go-zero v1.3.0/go.mod h1:Hy4o1VFAt32lXaQMbaBhoFeZjA/rJqJ4PTGNdGsURcc=
github.com/zeromicro/go-zero v1.3.2 h1:2HcmceZDEGwZWvofCG+0GXyh+Gtz/wKCW4Fq8Mb7KIg=
github.com/zeromicro/go-zero v1.3.2/go.mod h1:DEj3Fwj1Ui1ltsgf6YqwTL9nD4+tYzIRX0c1pWtQo1E= github.com/zeromicro/go-zero v1.3.2/go.mod h1:DEj3Fwj1Ui1ltsgf6YqwTL9nD4+tYzIRX0c1pWtQo1E=
github.com/zeromicro/go-zero v1.3.3 h1:6qv9PcfqfB1tMgp1FJlP1LfWSZ4XD+FwojvA2h5LL2k=
github.com/zeromicro/go-zero v1.3.3/go.mod h1:LwuYc2V04ZHhWPWGJYQ+kJ5DT4QSkeaZGqXiQcpkfks=
github.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20220228111653-d672d81f39ab h1:cgWxVhYV2Mr4ZClJTecxt0V5u5jvrayLzFYlUzoG5qI= github.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20220228111653-d672d81f39ab h1:cgWxVhYV2Mr4ZClJTecxt0V5u5jvrayLzFYlUzoG5qI=
github.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20220228111653-d672d81f39ab/go.mod h1:kM7gqMjv0B7QI1UsmJhN5UVWxQao9Oe3Jt3JnXc9o+c= github.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20220228111653-d672d81f39ab/go.mod h1:kM7gqMjv0B7QI1UsmJhN5UVWxQao9Oe3Jt3JnXc9o+c=
github.com/zeromicro/zero-contrib/zrpc/registry/nacos v0.0.0-20220525162615-f10f16d580d6 h1:2+DgXySLxSsB0LoYgk+AinZKnWq2mc5lxbqUQ7qTcvY=
github.com/zeromicro/zero-contrib/zrpc/registry/nacos v0.0.0-20220525162615-f10f16d580d6/go.mod h1:5yKg0EdtswP7sn1snNAkZ/6aj/zBYcyWOqTpLSAtW3M=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@ -718,23 +745,23 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL
go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q= go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q=
go.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA= go.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA=
go.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o= go.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=
go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= go.mongodb.org/mongo-driver v1.9.0 h1:f3aLGJvQmBl8d9S40IL+jEyBC6hfLPbJjv9t5hEM9ck=
go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.9.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.6.3 h1:FLOfo8f9JzFVFVyU+MSRJc2HdEAXQgm7pIv2uFKRSZE= go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
go.opentelemetry.io/otel/exporters/jaeger v1.3.0/go.mod h1:KoYHi1BtkUPncGSRtCe/eh1ijsnePhSkxwzz07vU0Fc= go.opentelemetry.io/otel/exporters/jaeger v1.3.0/go.mod h1:KoYHi1BtkUPncGSRtCe/eh1ijsnePhSkxwzz07vU0Fc=
go.opentelemetry.io/otel/exporters/zipkin v1.3.0/go.mod h1:LxGGfHIYbvsFnrJtBcazb0yG24xHdDGrT/H6RB9r3+8= go.opentelemetry.io/otel/exporters/zipkin v1.3.0/go.mod h1:LxGGfHIYbvsFnrJtBcazb0yG24xHdDGrT/H6RB9r3+8=
go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI= go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
go.opentelemetry.io/otel/trace v1.6.3 h1:IqN4L+5b0mPNjdXIiZ90Ni4Bl5BRkDQywePLWemd9bc= go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@ -784,8 +811,9 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -865,9 +893,11 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8=
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -886,8 +916,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -942,6 +973,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -952,14 +984,16 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@ -1103,8 +1137,9 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I=
google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731 h1:nquqdM9+ps0JZcIiI70+tqoaIFS5Ql4ZuK8UXnz3HfE=
google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -1124,8 +1159,10 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -1154,8 +1191,10 @@ gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaD
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@ -1170,8 +1209,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg= gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg=
gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI=
gorm.io/driver/postgres v1.2.1 h1:JDQKnF7MC51dgL09Vbydc5kl83KkVDlcXfSPJ+xhh68= gorm.io/driver/postgres v1.2.1 h1:JDQKnF7MC51dgL09Vbydc5kl83KkVDlcXfSPJ+xhh68=

6
helper/.goreleaser.yml

@ -12,9 +12,9 @@ builds:
dir: . dir: .
main: main.go main: main.go
ldflags: ldflags:
- -s -w -X main.Version={{.Version}} - -s -w -X main.Version=v{{.Version}}
- id: dtm_arm64 - id: dtm_arm64
env: [ CGO_ENABLED=0 ] env: [CGO_ENABLED=0]
goos: goos:
- darwin - darwin
goarch: goarch:
@ -22,4 +22,4 @@ builds:
dir: . dir: .
main: main.go main: main.go
ldflags: ldflags:
- -s -w -X main.Version={{.Version}} - -s -w -X main.Version=v{{.Version}}

11
helper/Dockerfile-release

@ -1,5 +1,11 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM --platform=$TARGETPLATFORM golang:1.16-alpine as builder # FROM node:14.19-alpine as builder1
# ARG RELEASE_VERSION
# WORKDIR /app/dtm
# COPY . .
# RUN cd admin && yarn && VITE_ADMIN_VERSION=$RELEASE_VERSION yarn build
FROM --platform=$TARGETPLATFORM golang:1.16-alpine as builder2
ARG TARGETARCH ARG TARGETARCH
ARG TARGETOS ARG TARGETOS
ARG RELEASE_VERSION ARG RELEASE_VERSION
@ -7,9 +13,10 @@ WORKDIR /app/dtm
# RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct # RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
EXPOSE 8080 EXPOSE 8080
COPY . . COPY . .
# COPY --from=builder1 /app/dtm/admin/dist /app/dtm/admin
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w -X main.Version=$RELEASE_VERSION" RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w -X main.Version=$RELEASE_VERSION"
FROM --platform=$TARGETPLATFORM alpine FROM --platform=$TARGETPLATFORM alpine
COPY --from=builder /app/dtm/dtm /app/dtm/ COPY --from=builder2 /app/dtm/dtm /app/dtm/
WORKDIR /app/dtm WORKDIR /app/dtm
ENTRYPOINT ["/app/dtm/dtm"] ENTRYPOINT ["/app/dtm/dtm"]

73
helper/README-cn.md

@ -9,14 +9,7 @@
# 跨语言分布式事务管理器 # 跨语言分布式事务管理器
DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方式,极大的降低了分布式事务的使用门槛,改变了“能不用分布式事务就不用”的行业现状。 dtm 的应用范围非常广,可以应用于以下常见的领域: DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方式,极大的降低了分布式事务的使用门槛,改变了“能不用分布式事务就不用”的行业现状,优雅的解决了服务间的数据一致性问题。
- [秒杀系统,保证Redis中精准的库存,和最终创建的订单完全一致,无需手动调整](https://dtm.pub/app/flash.html)
- [保证缓存与DB的一致性](https://dtm.pub/app/cache.html)
- [非单体的订单系统,大幅简化架构](https://dtm.pub/app/order.html)
- 微服务架构(已原生支持[go-zero](https://github.com/zeromicro/go-zero)等框架)中跨服务更新数据保证一致性
他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供跨语言,跨存储引擎组合事务的强大功能:
<img src="https://pica.zhimg.com/80/v2-2f66cb3074e68d38c29694318680acac_1440w.png" height=250 />
## 谁在使用DTM(仅列出部分) ## 谁在使用DTM(仅列出部分)
[Tencent 腾讯](https://dtm.pub/other/using.html#tencent) [Tencent 腾讯](https://dtm.pub/other/using.html#tencent)
@ -25,39 +18,26 @@ DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方
[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad) [Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)
## 亮点 [更多](https://dtm.pub/other/using.html)
* 极易上手
- 零配置启动服务,提供非常简单的HTTP接口,极大降低上手分布式事务的难度,新手也能快速接入
* 跨语言
- 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。
* 使用简单
- 开发者不再担心悬挂、空补偿、幂等各类问题,首创子事务屏障技术代为处理
* 易部署、易扩展
- 依赖mysql/redis,部署简单,易集群化,易水平扩展
* 多种分布式事务协议支持
- TCC、SAGA、XA、二阶段消息,一站式解决所有分布式事务问题
## 与其他框架对比
非Java语言类的,暂未看到除dtm之外的成熟框架,因此这里将DTM和Java中最成熟的Seata对比:
| 特性| DTM | SEATA |备注| 如果贵公司也已使用 dtm,欢迎在 [登记地址](https://github.com/dtm-labs/dtm/issues/7) 登记,仅仅为了推广,不做其它用途。
|:-----:|:----:|:----:|:----:|
|[支持语言](https://dtm.pub/other/opensource.html#lang) |<span style="color:green">Go、c#、Java、python、php...</span>|<span style="color:orange">Java</span>|dtm可轻松接入一门新语言|
|[存储引擎](https://dtm.pub/other/opensource.html#store) |<span style="color:green">支持数据库、Redis、Mongo等</span>|<span style="color:orange">数据库</span>||
|[异常处理](https://dtm.pub/other/opensource.html#exception)| <span style="color:green"> 子事务屏障自动处理 </span>|<span style="color:orange">手动处理</span> |dtm解决了幂等、悬挂、空补偿|
|[SAGA事务](https://dtm.pub/other/opensource.html#saga) |<span style="color:green">极简易用</span> |<span style="color:orange">复杂状态机</span> ||
|[二阶段消息](https://dtm.pub/other/opensource.html#msg)|<span style="color:green">✓</span>|<span style="color:red">✗</span>|最简消息最终一致性架构|
|[TCC事务](https://dtm.pub/other/opensource.html#tcc)| <span style="color:green">✓</span>|<span style="color:green">✓</span>||
|[XA事务](https://dtm.pub/other/opensource.html#xa)|<span style="color:green">✓</span>|<span style="color:green">✓</span>||
|[AT事务](https://dtm.pub/other/opensource.html#at)|<span style="color:orange">建议使用XA</span>|<span style="color:green">✓</span>|AT与XA类似,但有脏回滚|
|[单服务多数据源](https://dtm.pub/other/opensource.html#multidb)|<span style="color:green">✓</span>|<span style="color:red">✗</span>||
|[通信协议](https://dtm.pub/other/opensource.html#protocol)|HTTP、gRPC、go-zero|dubbo等协议|dtm对云原生更加友好|
从上面对比的特性来看,dtm在许多方面都具备很大的优势。如果考虑多语言支持、多存储引擎支持,那么dtm毫无疑问是您的首选 ## 特性
* 支持多种语言:支持Go、Java、PHP、C#、Python、Nodejs 各种语言的SDK
* 支持多种事务模式:SAGA、TCC、XA
* 支持消息最终一致性:二阶段消息,比本地消息表更优雅的方案
* 未支持 AT 事务模式,建议使用XA,详情参见[XA vs AT](https://dtm.pub/practice/at)
* 支持多种数据库事务:Mysql、Redis、MongoDB、Postgres、TDSQL等
* 支持多种存储引擎:Mysql(常用)、Redis(高性能)、MongoDB(规划中)
* 支持多种微服务架构:[go-zero](https://github.com/zeromicro/go-zero)、go-kratos/kratos、polarismesh/polaris
* 支持高可用,易水平扩展
详细的对比可以点击特性中的链接,跳到相关文档 ## 应用场景:
DTM 可以应用于大量的场景下的数据一致性问题,以下是几个常见场景
* [缓存管理](https://dtm.pub/app/cache.html):彻底保证缓存最终一致及强一致
* [秒杀扣库存](https://dtm.pub/app/flash.html):极端情况下,也能保证Redis中精准的库存,和最终创建的订单完全一致,无需手动调整
* [非单体的订单系统](https://dtm.pub/app/order.html): 大幅简化架构
* [事件发布/订阅](https://dtm.pub/practice/msg.html):更好的发件箱模式
## [性能测试报告](https://dtm.pub/other/performance.html) ## [性能测试报告](https://dtm.pub/other/performance.html)
@ -65,12 +45,6 @@ DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方
## [各语言客户端及示例](https://dtm.pub/ref/sdk.html#go) ## [各语言客户端及示例](https://dtm.pub/ref/sdk.html#go)
## 微服务框架支持
- [go-zero](https://github.com/zeromicro/go-zero):一开源就非常火爆的微服务框架,首家接入dtm的微服务框架。感谢go-zero作者[kevwan](https://github.com/kevwan)的大力支持
- [kratos](https://github.com/go-kratos/kratos):这是bilibili开源的一个微服务框架。感谢[lei liu](https://github.com/Leizhengzi)的贡献
- [polaris](https://github.com/polarismesh/polaris): 腾讯开源的注册发现组件,以及在其上构建的微服务框架。感谢腾讯同学[ychensha](https://github.com/ychensha)的PR
- 其他:看用户需求量,择机接入,参见[微服务支持](https://dtm.pub/ref/proto.html)
## 快速开始 ## 快速开始
如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/ref/sdk.html#go),里面有相关的快速开始示例 如果您不是Go语言,可以跳转[各语言客户端及示例](https://dtm.pub/ref/sdk.html#go),里面有相关的快速开始示例
@ -100,7 +74,7 @@ go run main.go
req := &gin.H{"amount": 30} // 微服务的载荷 req := &gin.H{"amount": 30} // 微服务的载荷
// DtmServer为DTM服务的地址,是一个url // DtmServer为DTM服务的地址,是一个url
DtmServer := "http://localhost:36789/api/dtmsvr" DtmServer := "http://localhost:36789/api/dtmsvr"
saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). saga := dtmcli.NewSaga(DtmServer, shortuuid.New()).
// 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCom" // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCom"
Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req).
// 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCom" // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCom"
@ -137,10 +111,11 @@ go run main.go
上述示例主要演示了分布式事务的流程,更多的内容,包括如何与实际的数据库对接,如何做补偿,如何做回滚等实际的例子,请参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples) 上述示例主要演示了分布式事务的流程,更多的内容,包括如何与实际的数据库对接,如何做补偿,如何做回滚等实际的例子,请参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)
## 联系我们 ## 联系我们
### 公众号 ### 微信交流群
dtm官方公众号:分布式事务,大量干货分享,以及dtm的最新消息
### 交流群 如果您希望更快的获得反馈,或者更多的了解其他用户在使用过程中的各种反馈,欢迎加入我们的微信交流群
请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群
请加作者的微信 yedf2008 好友或者扫码加好友,备注 `dtm` 按照指引进群
![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) ![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg)

10
helper/README-en.md

@ -17,11 +17,13 @@ DTM is a distributed transaction framework which provides cross-service eventual
## Who's using DTM (partial) ## Who's using DTM (partial)
[Tencent](https://www.tencent.com/) [Tencent](https://en.dtm.pub/other/using.html#tencent)
[Bytedance](https://www.bytedance.com/) [Bytedance](https://en.dtm.pub/other/using.html#bytedance)
[Ivydad](https://ivydad.com) [Ivydad](https://en.dtm.pub/other/using.html#ivydad)
[More](https://en.dtm.pub/other/using.html)
## [Cook Book](https://en.dtm.pub) ## [Cook Book](https://en.dtm.pub)
@ -54,7 +56,7 @@ go run main.go
DtmServer := "http://localhost:36789/api/dtmsvr" DtmServer := "http://localhost:36789/api/dtmsvr"
req := &gin.H{"amount": 30} // micro-service payload req := &gin.H{"amount": 30} // micro-service payload
// DtmServer is the address of DTM micro-service // DtmServer is the address of DTM micro-service
saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). saga := dtmcli.NewSaga(DtmServer, shortuuid.New()).
// add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCom" // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCom"
Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req).
// add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCom" // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCom"

16
helper/bench/svr/http.go

@ -53,7 +53,7 @@ func reloadData() {
db := pdbGet() db := pdbGet()
tables := []string{"dtm_busi.user_account", "dtm_busi.user_account_log", "dtm.trans_global", "dtm.trans_branch_op", "dtm_barrier.barrier"} tables := []string{"dtm_busi.user_account", "dtm_busi.user_account_log", "dtm.trans_global", "dtm.trans_branch_op", "dtm_barrier.barrier"}
for _, t := range tables { for _, t := range tables {
_, err := dtmimp.DBExec(db, fmt.Sprintf("truncate %s", t)) _, err := dtmimp.DBExec(busi.BusiConf.Driver, db, fmt.Sprintf("truncate %s", t))
logger.FatalIfError(err) logger.FatalIfError(err)
} }
s := "insert ignore into dtm_busi.user_account(user_id, balance) values " s := "insert ignore into dtm_busi.user_account(user_id, balance) values "
@ -61,7 +61,7 @@ func reloadData() {
for i := 1; i <= total; i++ { for i := 1; i <= total; i++ {
ss = append(ss, fmt.Sprintf("(%d, 1000000)", i)) ss = append(ss, fmt.Sprintf("(%d, 1000000)", i))
} }
_, err := dtmimp.DBExec(db, s+strings.Join(ss, ",")) _, err := dtmimp.DBExec(busi.BusiConf.Driver, db, s+strings.Join(ss, ","))
logger.FatalIfError(err) logger.FatalIfError(err)
logger.Debugf("%d users inserted. used: %dms", total, time.Since(began).Milliseconds()) logger.Debugf("%d users inserted. used: %dms", total, time.Since(began).Milliseconds())
} }
@ -73,11 +73,11 @@ var sqls = 1
// PrepareBenchDB prepares db data for bench // PrepareBenchDB prepares db data for bench
func PrepareBenchDB() { func PrepareBenchDB() {
db := pdbGet() db := pdbGet()
_, err := dtmimp.DBExec(db, "CREATE DATABASE if not exists dtm_busi") _, err := dtmimp.DBExec(busi.BusiConf.Driver, db, "CREATE DATABASE if not exists dtm_busi")
logger.FatalIfError(err) logger.FatalIfError(err)
_, err = dtmimp.DBExec(db, "drop table if exists dtm_busi.user_account_log") _, err = dtmimp.DBExec(busi.BusiConf.Driver, db, "drop table if exists dtm_busi.user_account_log")
logger.FatalIfError(err) logger.FatalIfError(err)
_, err = dtmimp.DBExec(db, `create table if not exists dtm_busi.user_account_log ( _, err = dtmimp.DBExec(busi.BusiConf.Driver, db, `create table if not exists dtm_busi.user_account_log (
id INT(11) AUTO_INCREMENT PRIMARY KEY, id INT(11) AUTO_INCREMENT PRIMARY KEY,
user_id INT(11) NOT NULL, user_id INT(11) NOT NULL,
delta DECIMAL(11, 2) not null, delta DECIMAL(11, 2) not null,
@ -111,10 +111,10 @@ func qsAdjustBalance(uid int, amount int, c *gin.Context) error { // nolint: unp
tb := dtmimp.TransBaseFromQuery(c.Request.URL.Query()) tb := dtmimp.TransBaseFromQuery(c.Request.URL.Query())
f := func(tx *sql.Tx) error { f := func(tx *sql.Tx) error {
for i := 0; i < sqls; i++ { for i := 0; i < sqls; i++ {
_, err := dtmimp.DBExec(tx, "insert into dtm_busi.user_account_log(user_id, delta, gid, branch_id, op, reason) values(?,?,?,?,?,?)", _, err := dtmimp.DBExec(busi.BusiConf.Driver, tx, "insert into dtm_busi.user_account_log(user_id, delta, gid, branch_id, op, reason) values(?,?,?,?,?,?)",
uid, amount, tb.Gid, c.Query("branch_id"), tb.TransType, fmt.Sprintf("inserted by dtm transaction %s %s", tb.Gid, c.Query("branch_id"))) uid, amount, tb.Gid, c.Query("branch_id"), tb.TransType, fmt.Sprintf("inserted by dtm transaction %s %s", tb.Gid, c.Query("branch_id")))
logger.FatalIfError(err) logger.FatalIfError(err)
_, err = dtmimp.DBExec(tx, "update dtm_busi.user_account set balance = balance + ?, update_time = now() where user_id = ?", amount, uid) _, err = dtmimp.DBExec(busi.BusiConf.Driver, tx, "update dtm_busi.user_account set balance = balance + ?, update_time = now() where user_id = ?", amount, uid)
logger.FatalIfError(err) logger.FatalIfError(err)
} }
return nil return nil
@ -122,7 +122,7 @@ func qsAdjustBalance(uid int, amount int, c *gin.Context) error { // nolint: unp
if strings.Contains(mode, "barrier") { if strings.Contains(mode, "barrier") {
barrier, err := dtmcli.BarrierFromQuery(c.Request.URL.Query()) barrier, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())
logger.FatalIfError(err) logger.FatalIfError(err)
err = barrier.Call(txGet(), f) err = barrier.CallWithDB(pdbGet(), f)
logger.FatalIfError(err) logger.FatalIfError(err)
} else { } else {
tx := txGet() tx := txGet()

28
helper/compose.cloud.yml

@ -1,28 +0,0 @@
version: '3.3'
services:
api:
build: ..
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ..:/app/dtm
extra_hosts:
- 'host.docker.internal:host-gateway'
environment:
IS_DOCKER: 1
ports:
- '9080:8080'
mysql:
image: 'mysql:5.7'
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 1
command:
[
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
]
ports:
- '3306:3306'

16
helper/compose.mysql.yml

@ -1,16 +0,0 @@
version: '3.3'
services:
mysql:
image: 'mysql:5.7'
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 1
command:
[
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
]
ports:
- '3306:3306'

13
helper/compose.postgres.yml

@ -1,13 +0,0 @@
version: '3.3'
services:
postgres:
image: 'postgres:13'
command: postgres --max_prepared_transactions=1000
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
environment:
POSTGRES_PASSWORD: mysecretpassword
ports:
- '5432:5432'

1
helper/compose.store.yml

@ -22,6 +22,7 @@ services:
- /etc/timezone:/etc/timezone:ro - /etc/timezone:/etc/timezone:ro
environment: environment:
POSTGRES_PASSWORD: mysecretpassword POSTGRES_PASSWORD: mysecretpassword
POSTGRES_DB: dtm
ports: ports:
- '5432:5432' - '5432:5432'

2
helper/golint.sh

@ -1,4 +1,4 @@
set -x set -x
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.41.0 curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2
$(go env GOPATH)/bin/golangci-lint run $(go env GOPATH)/bin/golangci-lint run

2
helper/test-cover.sh

@ -1,6 +1,6 @@
set -x set -x
echo "" > coverage.txt echo "" > coverage.txt
for store in redis mysql boltdb; do for store in redis mysql boltdb postgres; do
for d in $(go list ./... | grep -v vendor); do for d in $(go list ./... | grep -v vendor); do
TEST_STORE=$store go test -covermode count -coverprofile=profile.out -coverpkg=github.com/dtm-labs/dtm/dtmcli,github.com/dtm-labs/dtm/dtmcli/dtmimp,github.com/dtm-labs/dtm/dtmcli/logger,github.com/dtm-labs/dtm/dtmgrpc,github.com/dtm-labs/dtm/dtmgrpc/dtmgimp,github.com/dtm-labs/dtm/dtmsvr,github.com/dtm-labs/dtm/dtmsvr/config,github.com/dtm-labs/dtm/dtmsvr/storage,github.com/dtm-labs/dtm/dtmsvr/storage/boltdb,github.com/dtm-labs/dtm/dtmsvr/storage/redis,github.com/dtm-labs/dtm/dtmsvr/storage/registry,github.com/dtm-labs/dtm/dtmsvr/storage/sql,github.com/dtm-labs/dtm/dtmutil -gcflags=-l $d || exit 1 TEST_STORE=$store go test -covermode count -coverprofile=profile.out -coverpkg=github.com/dtm-labs/dtm/dtmcli,github.com/dtm-labs/dtm/dtmcli/dtmimp,github.com/dtm-labs/dtm/dtmcli/logger,github.com/dtm-labs/dtm/dtmgrpc,github.com/dtm-labs/dtm/dtmgrpc/dtmgimp,github.com/dtm-labs/dtm/dtmsvr,github.com/dtm-labs/dtm/dtmsvr/config,github.com/dtm-labs/dtm/dtmsvr/storage,github.com/dtm-labs/dtm/dtmsvr/storage/boltdb,github.com/dtm-labs/dtm/dtmsvr/storage/redis,github.com/dtm-labs/dtm/dtmsvr/storage/registry,github.com/dtm-labs/dtm/dtmsvr/storage/sql,github.com/dtm-labs/dtm/dtmutil -gcflags=-l $d || exit 1
if [ -f profile.out ]; then if [ -f profile.out ]; then

71
main.go

@ -7,12 +7,20 @@
package main package main
import ( import (
"embed"
"fmt" "fmt"
"io/fs"
"io/ioutil"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"os"
"os/signal"
"strings"
"syscall"
"github.com/dtm-labs/dtm/dtmcli/logger" "github.com/dtm-labs/dtm/dtmcli/logger"
"github.com/dtm-labs/dtm/dtmsvr/config"
"github.com/dtm-labs/dtm/dtmsvr/entry" "github.com/dtm-labs/dtm/dtmsvr/entry"
_ "github.com/dtm-labs/dtm/dtmsvr/microservices" _ "github.com/dtm-labs/dtm/dtmsvr/microservices"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -22,23 +30,61 @@ import (
var Version string var Version string
func main() { func main() {
app := entry.Main(&Version) app, conf := entry.Main(&Version)
if app != nil { if app != nil {
addDashboard(app) addAdmin(app, conf)
select {} q := make(chan os.Signal, 1)
signal.Notify(q, syscall.SIGINT, syscall.SIGTERM)
<-q
logger.Infof("Shutdown dtm server...")
} }
} }
func addDashboard(app *gin.Engine) {
app.GET("/dashboard/*name", proxyDashboard) //go:embed admin/dist
app.GET("/@vite/*name", proxyDashboard) var admin embed.FS
app.GET("/node_modules/*name", proxyDashboard)
app.GET("/src/*name", proxyDashboard) var target = ""
app.GET("/@id/*name", proxyDashboard)
func getSub(f1 fs.FS, sub string) fs.FS {
f2, err := fs.Sub(f1, sub)
logger.FatalIfError(err)
return f2
}
func addAdmin(app *gin.Engine, conf *config.Type) {
// for released dtm, serve admin from local files because the build output has been embed
// for testing users, proxy admin to target because the build output has not been embed
dist := getSub(admin, "admin/dist")
index, err := dist.Open("index.html")
if err == nil {
cont, err := ioutil.ReadAll(index)
logger.FatalIfError(err)
_ = index.Close()
sfile := string(cont)
renderIndex := func(c *gin.Context) {
c.Header("content-type", "text/html;charset=utf-8")
c.String(200, sfile)
}
app.StaticFS("/assets", http.FS(getSub(dist, "assets")))
app.GET("/admin/*name", renderIndex)
app.GET("/", renderIndex)
logger.Infof("admin is served from dir 'admin/dist/'")
} else {
app.GET("/", proxyAdmin)
app.GET("/assets/*name", proxyAdmin)
app.GET("/admin/*name", proxyAdmin)
lang := os.Getenv("LANG")
if strings.HasPrefix(lang, "zh_CN") {
target = "cn-admin.dtm.pub"
} else {
target = "admin.dtm.pub"
}
logger.Infof("admin is proxied to %s", target)
}
logger.Infof("admin is running at: http://localhost:%d", conf.HTTPPort)
} }
func proxyDashboard(c *gin.Context) { func proxyAdmin(c *gin.Context) {
target := "127.0.0.1:5000"
u := &url.URL{} u := &url.URL{}
u.Scheme = "http" u.Scheme = "http"
u.Host = target u.Host = target
@ -49,7 +95,8 @@ func proxyDashboard(c *gin.Context) {
ret := fmt.Sprintf("http proxy error %v", err) ret := fmt.Sprintf("http proxy error %v", err)
_, _ = rw.Write([]byte(ret)) _, _ = rw.Write([]byte(ret))
} }
logger.Debugf("proxy dashboard to %s", target) logger.Debugf("proxy admin to %s", target)
c.Request.Host = target
proxy.ServeHTTP(c.Writer, c.Request) proxy.ServeHTTP(c.Writer, c.Request)
} }

13
sqls/dtmsvr.storage.mysql.sql

@ -19,6 +19,7 @@ CREATE TABLE if not EXISTS dtm.trans_global (
`next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job', `next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job',
`owner` varchar(128) not null default '' comment 'who is locking this trans', `owner` varchar(128) not null default '' comment 'who is locking this trans',
`ext_data` TEXT comment 'extended data for this trans', `ext_data` TEXT comment 'extended data for this trans',
`rollback_reason` varchar(1024) DEFAULT '' COMMENT 'rollback reason for transaction',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `gid` (`gid`), UNIQUE KEY `gid` (`gid`),
key `owner`(`owner`), key `owner`(`owner`),
@ -41,3 +42,15 @@ CREATE TABLE IF NOT EXISTS dtm.trans_branch_op (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`) UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
drop table IF EXISTS dtm.kv;
CREATE TABLE IF NOT EXISTS dtm.kv (
`id` bigint(22) NOT NULL AUTO_INCREMENT,
`cat` varchar(45) NOT NULL COMMENT 'the category of this data',
`k` varchar(128) NOT NULL,
`v` TEXT,
`version` bigint(22) default 1 COMMENT 'version of the value',
create_time datetime default NULL,
update_time datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE key `uniq_k`(`cat`, `k`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

30
sqls/dtmsvr.storage.postgres.sql

@ -1,11 +1,8 @@
CREATE SCHEMA if not EXISTS dtm drop table IF EXISTS trans_global;
/* SQLINES DEMO *** RACTER SET utf8mb4 */
; CREATE SEQUENCE if not EXISTS trans_global_seq;
drop table IF EXISTS dtm.trans_global; CREATE TABLE if not EXISTS trans_global (
-- SQLINES LICENSE FOR EVALUATION USE ONLY id bigint NOT NULL DEFAULT NEXTVAL ('trans_global_seq'),
CREATE SEQUENCE if not EXISTS dtm.trans_global_seq;
CREATE TABLE if not EXISTS dtm.trans_global (
id bigint NOT NULL DEFAULT NEXTVAL ('dtm.trans_global_seq'),
gid varchar(128) NOT NULL, gid varchar(128) NOT NULL,
trans_type varchar(45) not null, trans_type varchar(45) not null,
status varchar(45) NOT NULL, status varchar(45) NOT NULL,
@ -21,16 +18,17 @@ CREATE TABLE if not EXISTS dtm.trans_global (
next_cron_time timestamp(0) with time zone default null, next_cron_time timestamp(0) with time zone default null,
owner varchar(128) not null default '', owner varchar(128) not null default '',
ext_data text, ext_data text,
rollback_reason varchar(1024) DEFAULT '',
PRIMARY KEY (id), PRIMARY KEY (id),
CONSTRAINT gid UNIQUE (gid) CONSTRAINT gid UNIQUE (gid)
); );
create index if not EXISTS owner on dtm.trans_global(owner); create index if not EXISTS owner on trans_global(owner);
create index if not EXISTS status_next_cron_time on dtm.trans_global (status, next_cron_time); create index if not EXISTS status_next_cron_time on trans_global (status, next_cron_time);
drop table IF EXISTS dtm.trans_branch_op; drop table IF EXISTS trans_branch_op;
-- SQLINES LICENSE FOR EVALUATION USE ONLY
CREATE SEQUENCE if not EXISTS dtm.trans_branch_op_seq; CREATE SEQUENCE if not EXISTS trans_branch_op_seq;
CREATE TABLE IF NOT EXISTS dtm.trans_branch_op ( CREATE TABLE IF NOT EXISTS trans_branch_op (
id bigint NOT NULL DEFAULT NEXTVAL ('dtm.trans_branch_op_seq'), id bigint NOT NULL DEFAULT NEXTVAL ('trans_branch_op_seq'),
gid varchar(128) NOT NULL, gid varchar(128) NOT NULL,
url varchar(1024) NOT NULL, url varchar(1024) NOT NULL,
data TEXT, data TEXT,
@ -44,4 +42,4 @@ CREATE TABLE IF NOT EXISTS dtm.trans_branch_op (
update_time timestamp(0) with time zone DEFAULT NULL, update_time timestamp(0) with time zone DEFAULT NULL,
PRIMARY KEY (id), PRIMARY KEY (id),
CONSTRAINT gid_branch_uniq UNIQUE (gid, branch_id, op) CONSTRAINT gid_branch_uniq UNIQUE (gid, branch_id, op)
); );

1
sqls/dtmsvr.storage.tdsql.sql

@ -19,6 +19,7 @@ CREATE TABLE if not EXISTS dtm.trans_global (
`next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job', `next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job',
`owner` varchar(128) not null default '' comment 'who is locking this trans', `owner` varchar(128) not null default '' comment 'who is locking this trans',
`ext_data` TEXT comment 'extended data for this trans', `ext_data` TEXT comment 'extended data for this trans',
`rollback_reason` varchar(1024) DEFAULT '' COMMENT 'rollback reason for transaction',
PRIMARY KEY (`id`,`gid`), PRIMARY KEY (`id`,`gid`),
UNIQUE KEY `id` (`id`,`gid`), UNIQUE KEY `id` (`id`,`gid`),
UNIQUE KEY `gid` (`gid`), UNIQUE KEY `gid` (`gid`),

6
test/api_test.go

@ -18,6 +18,12 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestAPIVersion(t *testing.T) {
resp, err := dtmimp.RestyClient.R().Get(dtmutil.DefaultHTTPServer + "/version")
assert.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode())
}
func TestAPIQuery(t *testing.T) { func TestAPIQuery(t *testing.T) {
gid := dtmimp.GetFuncName() gid := dtmimp.GetFuncName()
err := genMsg(gid).Submit() err := genMsg(gid).Submit()

4
test/base_test.go

@ -26,7 +26,7 @@ type BarrierModel struct {
} }
// TableName gorm table name // TableName gorm table name
func (BarrierModel) TableName() string { return "dtm_barrier.barrier" } func (BarrierModel) TableName() string { return dtmimp.BarrierTableName }
func TestBaseSqlDB(t *testing.T) { func TestBaseSqlDB(t *testing.T) {
asserts := assert.New(t) asserts := assert.New(t)
@ -38,7 +38,7 @@ func TestBaseSqlDB(t *testing.T) {
Op: dtmimp.OpAction, Op: dtmimp.OpAction,
BarrierID: 1, BarrierID: 1,
} }
db.Must().Exec("insert into dtm_barrier.barrier(trans_type, gid, branch_id, op, barrier_id, reason) values('saga', 'gid1', 'branch_id1', 'action', '01', 'saga')") db.Must().Exec(fmt.Sprintf("insert into %s(trans_type, gid, branch_id, op, barrier_id, reason) values('saga', 'gid1', 'branch_id1', 'action', '01', 'saga')", dtmimp.BarrierTableName))
tx, err := db.ToSQLDB().Begin() tx, err := db.ToSQLDB().Begin()
asserts.Nil(err) asserts.Nil(err)
err = barrier.Call(tx, func(tx *sql.Tx) error { err = barrier.Call(tx, func(tx *sql.Tx) error {

34
test/busi/barrier.go

@ -22,49 +22,49 @@ func init() {
setupFuncs["BarrierSetup"] = func(app *gin.Engine) { setupFuncs["BarrierSetup"] = func(app *gin.Engine) {
app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
barrier := MustBarrierFromGin(c) barrier := MustBarrierFromGin(c)
return barrier.Call(txGet(), func(tx *sql.Tx) error { return barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult) return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)
}) })
})) }))
app.POST(BusiAPI+"/SagaBTransInCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/SagaBTransInCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
barrier := MustBarrierFromGin(c) barrier := MustBarrierFromGin(c)
return barrier.Call(txGet(), func(tx *sql.Tx) error { return barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, "") return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, "")
}) })
})) }))
app.POST(BusiAPI+"/SagaB2TransIn", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/SagaB2TransIn", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
barrier := MustBarrierFromGin(c) barrier := MustBarrierFromGin(c)
err := barrier.Call(txGet(), func(tx *sql.Tx) error { err := barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount/2, reqFrom(c).TransInResult) return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount/2, reqFrom(c).TransInResult)
}) })
if err != nil { if err != nil {
return err return err
} }
return barrier.Call(txGet(), func(tx *sql.Tx) error { return barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount/2, reqFrom(c).TransInResult) return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount/2, reqFrom(c).TransInResult)
}) })
})) }))
app.POST(BusiAPI+"/SagaB2TransInCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/SagaB2TransInCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
barrier := MustBarrierFromGin(c) barrier := MustBarrierFromGin(c)
err := barrier.Call(txGet(), func(tx *sql.Tx) error { err := barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount/2, "") return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount/2, "")
}) })
if err != nil { if err != nil {
return err return err
} }
return barrier.Call(txGet(), func(tx *sql.Tx) error { return barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, "") return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, "")
}) })
})) }))
app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
barrier := MustBarrierFromGin(c) barrier := MustBarrierFromGin(c)
return barrier.Call(txGet(), func(tx *sql.Tx) error { return barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult) return SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)
}) })
})) }))
app.POST(BusiAPI+"/SagaBTransOutCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/SagaBTransOutCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
barrier := MustBarrierFromGin(c) barrier := MustBarrierFromGin(c)
return barrier.Call(txGet(), func(tx *sql.Tx) error { return barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return SagaAdjustBalance(tx, TransOutUID, reqFrom(c).Amount, "") return SagaAdjustBalance(tx, TransOutUID, reqFrom(c).Amount, "")
}) })
})) }))
@ -82,17 +82,17 @@ func init() {
if req.TransInResult != "" { if req.TransInResult != "" {
return dtmcli.String2DtmError(req.TransInResult) return dtmcli.String2DtmError(req.TransInResult)
} }
return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error { return MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransInUID, req.Amount) return tccAdjustTrading(tx, TransInUID, req.Amount)
}) })
})) }))
app.POST(BusiAPI+"/TccBTransInConfirm", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/TccBTransInConfirm", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error { return MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return tccAdjustBalance(tx, TransInUID, reqFrom(c).Amount) return tccAdjustBalance(tx, TransInUID, reqFrom(c).Amount)
}) })
})) }))
app.POST(BusiAPI+"/TccBTransInCancel", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/TccBTransInCancel", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error { return MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransInUID, -reqFrom(c).Amount) return tccAdjustTrading(tx, TransInUID, -reqFrom(c).Amount)
}) })
})) }))
@ -170,7 +170,7 @@ func init() {
}) })
} }
return bb.Call(txGet(), func(tx *sql.Tx) error { return bb.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransOutUID, -req.Amount) return tccAdjustTrading(tx, TransOutUID, -req.Amount)
}) })
})) }))
@ -178,7 +178,7 @@ func init() {
if reqFrom(c).Store == Redis || reqFrom(c).Store == Mongo { if reqFrom(c).Store == Redis || reqFrom(c).Store == Mongo {
return nil return nil
} }
return MustBarrierFromGin(c).Call(txGet(), func(tx *sql.Tx) error { return MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return tccAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount) return tccAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount)
}) })
})) }))
@ -198,7 +198,7 @@ func TccBarrierTransOutCancel(c *gin.Context) interface{} {
return SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, reqFrom(c).Amount, "") return SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, reqFrom(c).Amount, "")
}) })
} }
return bb.Call(txGet(), func(tx *sql.Tx) error { return bb.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransOutUID, reqFrom(c).Amount) return tccAdjustTrading(tx, TransOutUID, reqFrom(c).Amount)
}) })
} }
@ -212,21 +212,21 @@ func (s *busiServer) TransInBSaga(ctx context.Context, in *BusiReq) (*emptypb.Em
func (s *busiServer) TransOutBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) { func (s *busiServer) TransOutBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
barrier := MustBarrierFromGrpc(ctx) barrier := MustBarrierFromGrpc(ctx)
return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error { return &emptypb.Empty{}, barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return sagaGrpcAdjustBalance(tx, TransOutUID, -in.Amount, in.TransOutResult) return sagaGrpcAdjustBalance(tx, TransOutUID, -in.Amount, in.TransOutResult)
}) })
} }
func (s *busiServer) TransInRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) { func (s *busiServer) TransInRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
barrier := MustBarrierFromGrpc(ctx) barrier := MustBarrierFromGrpc(ctx)
return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error { return &emptypb.Empty{}, barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return sagaGrpcAdjustBalance(tx, TransInUID, -in.Amount, "") return sagaGrpcAdjustBalance(tx, TransInUID, -in.Amount, "")
}) })
} }
func (s *busiServer) TransOutRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) { func (s *busiServer) TransOutRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) {
barrier := MustBarrierFromGrpc(ctx) barrier := MustBarrierFromGrpc(ctx)
return &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error { return &emptypb.Empty{}, barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {
return sagaGrpcAdjustBalance(tx, TransOutUID, in.Amount, "") return sagaGrpcAdjustBalance(tx, TransOutUID, in.Amount, "")
}) })
} }

5
test/busi/base_http.go

@ -69,7 +69,8 @@ func BaseAppStartup() *gin.Engine {
} }
logger.Debugf("Starting busi at: %d", BusiPort) logger.Debugf("Starting busi at: %d", BusiPort)
go func() { go func() {
_ = app.Run(fmt.Sprintf(":%d", BusiPort)) err := app.Run(fmt.Sprintf(":%d", BusiPort))
dtmimp.FatalIfError(err)
}() }()
return app return app
} }
@ -140,7 +141,7 @@ func BaseAddRoute(app *gin.Engine) {
})) }))
app.POST(BusiAPI+"/TransOutXa", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/TransOutXa", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
return dtmcli.XaLocalTransaction(c.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error { return dtmcli.XaLocalTransaction(c.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error {
return SagaAdjustBalance(db, TransOutUID, reqFrom(c).Amount, reqFrom(c).TransOutResult) return SagaAdjustBalance(db, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)
}) })
})) }))
app.POST(BusiAPI+"/TransOutTimeout", dtmutil.WrapHandler(func(c *gin.Context) interface{} { app.POST(BusiAPI+"/TransOutTimeout", dtmutil.WrapHandler(func(c *gin.Context) interface{} {

1
test/busi/base_types.go

@ -142,6 +142,7 @@ type mainSwitchType struct {
QueryPreparedResult AutoEmptyString QueryPreparedResult AutoEmptyString
NextResult AutoEmptyString NextResult AutoEmptyString
JrpcResult AutoEmptyString JrpcResult AutoEmptyString
FailureReason AutoEmptyString
} }
// MainSwitch controls busi success or fail // MainSwitch controls busi success or fail

20
test/busi/busi.go

@ -34,7 +34,7 @@ func handleGrpcBusiness(in *BusiReq, result1 string, result2 string, busi string
if res == dtmcli.ResultSuccess { if res == dtmcli.ResultSuccess {
return nil return nil
} else if res == dtmcli.ResultFailure { } else if res == dtmcli.ResultFailure {
return status.New(codes.Aborted, dtmcli.ResultFailure).Err() return status.New(codes.Aborted, fmt.Sprintf("reason:%s", MainSwitch.FailureReason.Fetch())).Err()
} else if res == dtmcli.ResultOngoing { } else if res == dtmcli.ResultOngoing {
return status.New(codes.FailedPrecondition, dtmcli.ResultOngoing).Err() return status.New(codes.FailedPrecondition, dtmcli.ResultOngoing).Err()
} }
@ -48,6 +48,9 @@ func handleGeneralBusiness(c *gin.Context, result1 string, result2 string, busi
if res == "ERROR" { if res == "ERROR" {
return errors.New("ERROR from user") return errors.New("ERROR from user")
} }
if res == dtmimp.ResultFailure {
return fmt.Errorf("reason:%s. %w", MainSwitch.FailureReason.Fetch(), dtmimp.ErrFailure)
}
return dtmcli.String2DtmError(res) return dtmcli.String2DtmError(res)
} }
@ -66,7 +69,7 @@ func sagaGrpcAdjustBalance(db dtmcli.DB, uid int, amount int64, result string) e
if result == dtmcli.ResultFailure { if result == dtmcli.ResultFailure {
return status.New(codes.Aborted, dtmcli.ResultFailure).Err() return status.New(codes.Aborted, dtmcli.ResultFailure).Err()
} }
_, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid) _, err := dtmimp.DBExec(BusiConf.Driver, db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
return err return err
} }
@ -75,7 +78,7 @@ func SagaAdjustBalance(db dtmcli.DB, uid int, amount int, result string) error {
if strings.Contains(result, dtmcli.ResultFailure) { if strings.Contains(result, dtmcli.ResultFailure) {
return dtmcli.ErrFailure return dtmcli.ErrFailure
} }
_, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid) _, err := dtmimp.DBExec(BusiConf.Driver, db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
return err return err
} }
@ -102,12 +105,12 @@ func SagaMongoAdjustBalance(ctx context.Context, mc *mongo.Client, uid int, amou
return fmt.Errorf("balance not enough %w", dtmcli.ErrFailure) return fmt.Errorf("balance not enough %w", dtmcli.ErrFailure)
} }
return nil return nil
} }
func tccAdjustTrading(db dtmcli.DB, uid int, amount int) error { func tccAdjustTrading(db dtmcli.DB, uid int, amount int) error {
affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance+? affected, err := dtmimp.DBExec(BusiConf.Driver, db, `update dtm_busi.user_account
where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount) set trading_balance=trading_balance+?
where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount)
if err == nil && affected == 0 { if err == nil && affected == 0 {
return fmt.Errorf("update error, maybe balance not enough") return fmt.Errorf("update error, maybe balance not enough")
} }
@ -115,8 +118,9 @@ func tccAdjustTrading(db dtmcli.DB, uid int, amount int) error {
} }
func tccAdjustBalance(db dtmcli.DB, uid int, amount int) error { func tccAdjustBalance(db dtmcli.DB, uid int, amount int) error {
affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance-?, affected, err := dtmimp.DBExec(BusiConf.Driver, db, `update dtm_busi.user_account
balance=balance+? where user_id=?`, amount, amount, uid) set trading_balance=trading_balance-?,
balance=balance+? where user_id=?`, amount, amount, uid)
if err == nil && affected == 0 { if err == nil && affected == 0 {
return fmt.Errorf("update user_account 0 rows") return fmt.Errorf("update user_account 0 rows")
} }

3
test/busi/quick_start.go

@ -7,6 +7,7 @@ import (
"github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/lithammer/shortuuid/v3"
) )
// busi address // busi address
@ -59,7 +60,7 @@ const dtmServer = "http://localhost:36789/api/dtmsvr"
func QsFireRequest() string { func QsFireRequest() string {
req := &gin.H{"amount": 30} // load of micro-service req := &gin.H{"amount": 30} // load of micro-service
// DtmServer is the url of dtm // DtmServer is the url of dtm
saga := dtmcli.NewSaga(dtmServer, dtmcli.MustGenGid(dtmServer)). saga := dtmcli.NewSaga(dtmServer, shortuuid.New()).
// add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate" // add a TransOut subtraction,forward operation with url: qsBusi+"/TransOut", reverse compensation operation with url: qsBusi+"/TransOutCompensate"
Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req).
// add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate" // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate"

6
test/common_test.go

@ -33,12 +33,12 @@ func testSql(t *testing.T) {
func testDbAlone(t *testing.T) { func testDbAlone(t *testing.T) {
db, err := dtmimp.StandaloneDB(conf.Store.GetDBConf()) db, err := dtmimp.StandaloneDB(conf.Store.GetDBConf())
assert.Nil(t, err) assert.Nil(t, err)
_, err = dtmimp.DBExec(db, "select 1") _, err = dtmimp.DBExec(conf.Store.Driver, db, "select 1")
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
_, err = dtmimp.DBExec(db, "") _, err = dtmimp.DBExec(conf.Store.Driver, db, "")
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
db.Close() db.Close()
_, err = dtmimp.DBExec(db, "select 1") _, err = dtmimp.DBExec(conf.Store.Driver, db, "select 1")
assert.NotEqual(t, nil, err) assert.NotEqual(t, nil, err)
} }

4
test/dtmsvr_test.go

@ -26,6 +26,10 @@ func getTransStatus(gid string) string {
return dtmsvr.GetTransGlobal(gid).Status return dtmsvr.GetTransGlobal(gid).Status
} }
func getTrans(gid string) *dtmsvr.TransGlobal {
return dtmsvr.GetTransGlobal(gid)
}
func getBranchesStatus(gid string) []string { func getBranchesStatus(gid string) []string {
branches := dtmsvr.GetStore().FindBranches(gid) branches := dtmsvr.GetStore().FindBranches(gid)
status := []string{} status := []string{}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save