diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b698e42..2edc31e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,14 @@ jobs: name: Release on GitHub runs-on: ubuntu-latest 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 - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Validates GO releaser config uses: docker://goreleaser/goreleaser:v1.7.0 @@ -24,7 +30,38 @@ jobs: env: 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 uses: docker/metadata-action@v3 with: @@ -48,9 +85,6 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get Release Version - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - name: Build and push uses: docker/build-push-action@v2 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e71699d..d12fe2a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,6 +28,16 @@ jobs: - /etc/timezone:/etc/timezone:ro ports: - 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: image: 'yedf/mongo-rs' volumes: diff --git a/.gitignore b/.gitignore index f0476cb..135fe3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ conf.yml *.out *.log -dist +# dist .idea/** .vscode default.etcd diff --git a/README.md b/README.md index 5e49007..1c7e181 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,17 @@ DTM is a distributed transaction framework which provides cross-service eventual function-picture +## Features + ## 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) @@ -54,7 +58,7 @@ go run main.go DtmServer := "http://localhost:36789/api/dtmsvr" req := &gin.H{"amount": 30} // micro-service payload // 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(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCom" diff --git a/admin/.env b/admin/.env new file mode 100644 index 0000000..fde6533 --- /dev/null +++ b/admin/.env @@ -0,0 +1 @@ +VITE_ADMIN_VERSION="v0.0.0-dev" diff --git a/dashboard/.eslintrc.js b/admin/.eslintrc.js similarity index 100% rename from dashboard/.eslintrc.js rename to admin/.eslintrc.js diff --git a/dashboard/.gitignore b/admin/.gitignore similarity index 100% rename from dashboard/.gitignore rename to admin/.gitignore diff --git a/dashboard/README.md b/admin/README.md similarity index 100% rename from dashboard/README.md rename to admin/README.md diff --git a/admin/dist/placeholder b/admin/dist/placeholder new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/admin/dist/placeholder @@ -0,0 +1 @@ + diff --git a/dashboard/index.html b/admin/index.html similarity index 100% rename from dashboard/index.html rename to admin/index.html diff --git a/dashboard/package.json b/admin/package.json similarity index 92% rename from dashboard/package.json rename to admin/package.json index 8be934b..e5101f4 100644 --- a/dashboard/package.json +++ b/admin/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "scripts": { "dev": "vite", - "build": "vite build", + "build": "vite build && echo > dist/placeholder", "preview": "vite preview" }, "dependencies": { @@ -28,6 +28,7 @@ "postcss-import": "^14.1.0", "postcss-nested": "^5.0.6", "postcss-simple-vars": "^6.0.3", + "screenfull": "^6.0.1", "tailwindcss": "^3.0.24", "typescript": "^4.5.4", "unplugin-vue-components": "^0.19.3", diff --git a/dashboard/postcss.config.js b/admin/postcss.config.js similarity index 100% rename from dashboard/postcss.config.js rename to admin/postcss.config.js diff --git a/dashboard/public/favicon.ico b/admin/public/favicon.ico similarity index 100% rename from dashboard/public/favicon.ico rename to admin/public/favicon.ico diff --git a/dashboard/src/App.vue b/admin/src/App.vue similarity index 100% rename from dashboard/src/App.vue rename to admin/src/App.vue diff --git a/admin/src/api/api_dtm.ts b/admin/src/api/api_dtm.ts new file mode 100644 index 0000000..20f368a --- /dev/null +++ b/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(payload: IListAllTransactionsReq): Promise> { + return request({ + url: '/api/dtmsvr/all', + method: 'get', + params: payload + }) +} + +export function forceStopTransaction(gid: string): Promise { + return request({ + url: '/api/dtmsvr/forceStop', + method: 'post', + data: { gid }, + }) +} + +export function getTransaction(payload: { gid: string }): Promise> { + return request({ + url: '/api/dtmsvr/query', + method: 'get', + params: payload + }) +} + +export function getDtmVersion(): Promise> { + return request({ + url: '/api/dtmsvr/version', + method: 'get', + }) +} diff --git a/dashboard/src/assets/css/index.css b/admin/src/assets/css/index.css similarity index 100% rename from dashboard/src/assets/css/index.css rename to admin/src/assets/css/index.css diff --git a/dashboard/src/components.d.ts b/admin/src/components.d.ts similarity index 82% rename from dashboard/src/components.d.ts rename to admin/src/components.d.ts index 1c6807f..eb0b33b 100644 --- a/dashboard/src/components.d.ts +++ b/admin/src/components.d.ts @@ -5,6 +5,7 @@ import '@vue/runtime-core' declare module '@vue/runtime-core' { export interface GlobalComponents { + AAlert: typeof import('ant-design-vue/es')['Alert'] ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] 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'] AMenu: typeof import('ant-design-vue/es')['Menu'] AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] + AModal: typeof import('ant-design-vue/es')['Modal'] ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] ATable: typeof import('ant-design-vue/es')['Table'] ATag: typeof import('ant-design-vue/es')['Tag'] + ATextarea: typeof import('ant-design-vue/es')['Textarea'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + Screenfull: typeof import('./components/Screenfull/index.vue')['default'] SvgIcon: typeof import('./components/SvgIcon/index.vue')['default'] } } diff --git a/admin/src/components/Screenfull/index.vue b/admin/src/components/Screenfull/index.vue new file mode 100644 index 0000000..6f12919 --- /dev/null +++ b/admin/src/components/Screenfull/index.vue @@ -0,0 +1,40 @@ + + diff --git a/dashboard/src/components/SvgIcon/index.vue b/admin/src/components/SvgIcon/index.vue similarity index 96% rename from dashboard/src/components/SvgIcon/index.vue rename to admin/src/components/SvgIcon/index.vue index f6fd3a5..29bd947 100644 --- a/dashboard/src/components/SvgIcon/index.vue +++ b/admin/src/components/SvgIcon/index.vue @@ -30,6 +30,8 @@ const styleExternalIcon = () => { diff --git a/dashboard/src/layout/components/sidebar.vue b/admin/src/layout/components/sidebar.vue similarity index 100% rename from dashboard/src/layout/components/sidebar.vue rename to admin/src/layout/components/sidebar.vue diff --git a/dashboard/src/layout/index.vue b/admin/src/layout/index.vue similarity index 100% rename from dashboard/src/layout/index.vue rename to admin/src/layout/index.vue diff --git a/admin/src/main.ts b/admin/src/main.ts new file mode 100644 index 0000000..26dffc7 --- /dev/null +++ b/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) + } +} \ No newline at end of file diff --git a/dashboard/src/permission.ts b/admin/src/permission.ts similarity index 100% rename from dashboard/src/permission.ts rename to admin/src/permission.ts diff --git a/dashboard/src/router/asyncRouter.ts b/admin/src/router/asyncRouter.ts similarity index 100% rename from dashboard/src/router/asyncRouter.ts rename to admin/src/router/asyncRouter.ts diff --git a/admin/src/router/index.ts b/admin/src/router/index.ts new file mode 100644 index 0000000..dbc0371 --- /dev/null +++ b/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> = Object.assign({}, components, { + LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise, + LayoutMain: (() => import('/@/layout/aside.vue')) as unknown as () => Promise +}) + +export const allowRouter: Array = [ + { + 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 diff --git a/dashboard/src/store/index.ts b/admin/src/store/index.ts similarity index 100% rename from dashboard/src/store/index.ts rename to admin/src/store/index.ts diff --git a/dashboard/src/store/modules/layout.ts b/admin/src/store/modules/layout.ts similarity index 64% rename from dashboard/src/store/modules/layout.ts rename to admin/src/store/modules/layout.ts index 135fa36..0e2d8a5 100644 --- a/dashboard/src/store/modules/layout.ts +++ b/admin/src/store/modules/layout.ts @@ -1,17 +1,19 @@ import { defineStore } from 'pinia'; import { allowRouter } from '/@/router'; import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'; -import { findCurrentMenubar } from '/@/utils/util'; +import { getDtmVersion } from '/@/api/api_dtm'; export const useLayoutStore = defineStore({ id: 'layout', - state: ():ILayout => ({ + state: (): ILayout => ({ menubar: { menuList: [] }, status: { isLoading: false }, + dtmVersion: "", + globalError: "", }), getters: { getMenubar(): IMenubar { @@ -25,8 +27,16 @@ export const useLayoutStore = defineStore({ setRoutes(data: Array): void { this.menubar.menuList = data }, + setGlobalError(err: string) { + this.globalError = err + }, concatAllowRoutes(): void { allowRouter.reverse().forEach(v => this.menubar.menuList.unshift(v)) }, + async loadDtmVersion(): Promise { + const { data: { version } } = await getDtmVersion() + this.dtmVersion = version + console.log("dtm version: ", this.dtmVersion) + } } }) diff --git a/dashboard/src/type/index.d.ts b/admin/src/type/index.d.ts similarity index 66% rename from dashboard/src/type/index.d.ts rename to admin/src/type/index.d.ts index 8ac4521..53b355b 100644 --- a/dashboard/src/type/index.d.ts +++ b/admin/src/type/index.d.ts @@ -1,4 +1,4 @@ -export {} +export { } declare global { interface IObject { [index: string]: T @@ -7,10 +7,11 @@ declare global { VITE_APP_TITLE: string VITE_PORT: number VITE_PROXY: string + VITE_ADMIN_VERSION: string } interface ITable { - data: Array - next_position: number, - size: number + data: Array + next_position: number, + size: number } } diff --git a/dashboard/src/type/shim.vue.d.ts b/admin/src/type/shim.vue.d.ts similarity index 100% rename from dashboard/src/type/shim.vue.d.ts rename to admin/src/type/shim.vue.d.ts diff --git a/dashboard/src/type/store/layout.ts b/admin/src/type/store/layout.ts similarity index 74% rename from dashboard/src/type/store/layout.ts rename to admin/src/type/store/layout.ts index a7121bd..e2f7ea7 100644 --- a/dashboard/src/type/store/layout.ts +++ b/admin/src/type/store/layout.ts @@ -1,14 +1,16 @@ export interface IMenubar { - menuList: Array + menuList: Array } export interface ILayout { - menubar: IMenubar - status: IStatus + menubar: IMenubar + status: IStatus + dtmVersion: string + globalError: string } export interface IStatus { - isLoading: boolean + isLoading: boolean } export interface IMenubarList { @@ -16,7 +18,7 @@ export interface IMenubarList { id?: number | string name: string path: string - redirect?: string + redirect?: string meta: { icon?: string title: string diff --git a/dashboard/src/utils/request.ts b/admin/src/utils/request.ts similarity index 58% rename from dashboard/src/utils/request.ts rename to admin/src/utils/request.ts index 09b32db..1ce371f 100644 --- a/dashboard/src/utils/request.ts +++ b/admin/src/utils/request.ts @@ -1,7 +1,6 @@ import axios from 'axios' const request = axios.create({ - baseURL: import.meta.env.VITE_APP_API_BASE_URL as string | undefined, timeout: 60000 }) diff --git a/dashboard/src/utils/util.ts b/admin/src/utils/util.ts similarity index 79% rename from dashboard/src/utils/util.ts rename to admin/src/utils/util.ts index 43dc575..1f2b118 100644 --- a/dashboard/src/utils/util.ts +++ b/admin/src/utils/util.ts @@ -1,9 +1,9 @@ import { useRoute } from 'vue-router'; import { IMenubarList } from '../type/store/layout'; -export const findCurrentMenubar = (menuList: IMenubarList[], root?:boolean) => { +export const findCurrentMenubar = (menuList: IMenubarList[], root?: boolean) => { const route = useRoute() - let arr:IMenubarList[] | IMenubarList = [] + let arr: IMenubarList[] | IMenubarList = [] for (let i = 0; i < menuList.length; i++) { const v = menuList[i]; const usePath = v.meta.activeMenu || v.redirect || v.path; @@ -21,3 +21,7 @@ export const findCurrentMenubar = (menuList: IMenubarList[], root?:boolean) => { return arr } + +export const sleep = async (ms: number) => { + return new Promise(resolve => setTimeout(resolve, ms)) +} \ No newline at end of file diff --git a/dashboard/src/views/Dashboard/GlobalTransactions/AllTransactions.vue b/admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue similarity index 61% rename from dashboard/src/views/Dashboard/GlobalTransactions/AllTransactions.vue rename to admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue index 7f70c4d..e57924d 100644 --- a/dashboard/src/views/Dashboard/GlobalTransactions/AllTransactions.vue +++ b/admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue @@ -4,13 +4,14 @@ @@ -19,13 +20,15 @@ Previous Next + + diff --git a/dashboard/src/views/Dashboard/Nodes/LivingNodes.vue b/admin/src/views/Dashboard/Nodes/LivingNodes.vue similarity index 100% rename from dashboard/src/views/Dashboard/Nodes/LivingNodes.vue rename to admin/src/views/Dashboard/Nodes/LivingNodes.vue diff --git a/dashboard/tailwind.config.js b/admin/tailwind.config.js similarity index 100% rename from dashboard/tailwind.config.js rename to admin/tailwind.config.js diff --git a/dashboard/tsconfig.json b/admin/tsconfig.json similarity index 100% rename from dashboard/tsconfig.json rename to admin/tsconfig.json diff --git a/dashboard/vite.config.ts b/admin/vite.config.ts similarity index 96% rename from dashboard/vite.config.ts rename to admin/vite.config.ts index 18e1f84..df61c87 100644 --- a/dashboard/vite.config.ts +++ b/admin/vite.config.ts @@ -28,8 +28,8 @@ export default ({ }: ConfigEnv): UserConfigExport => { }) ], server: { - port: 5000, - base: 'dashboard', + port: 6789, + base: 'admin', proxy: { '/api': { target: 'http://localhost:36789', diff --git a/dashboard/yarn.lock b/admin/yarn.lock similarity index 99% rename from dashboard/yarn.lock rename to admin/yarn.lock index 76a90b5..6458aac 100644 --- a/dashboard/yarn.lock +++ b/admin/yarn.lock @@ -2681,6 +2681,11 @@ safe-regex@^1.1.0: dependencies: 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: version "2.2.29" resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz" diff --git a/conf.sample.yml b/conf.sample.yml index 1427da2..a256a2a 100644 --- a/conf.sample.yml +++ b/conf.sample.yml @@ -11,6 +11,7 @@ # User: 'root' # Password: '' # Port: 3306 +# Db: 'dtm' # Driver: 'boltdb' # default store engine @@ -30,8 +31,6 @@ # MaxOpenConns: 500 # MaxIdleConns: 500 # ConnMaxLifeTime: 5 # default value is 5 (minutes) -# TransGlobalTable: 'dtm.trans_global' -# TransBranchOpTable: 'dtm.trans_branch_op' ### flollowing config is only for some Driver # DataExpire: 604800 # Trans data will expire in 7 days. only for redis/boltdb. diff --git a/dashboard/.env.development b/dashboard/.env.development deleted file mode 100644 index 88c4eb8..0000000 --- a/dashboard/.env.development +++ /dev/null @@ -1,2 +0,0 @@ -VITE_APP_API_BASE_URL="/api" -VITE_PROXY=[["/api", "http://localhost:36789"]] diff --git a/dashboard/src/layout/components/content.vue b/dashboard/src/layout/components/content.vue deleted file mode 100644 index 0f8ba74..0000000 --- a/dashboard/src/layout/components/content.vue +++ /dev/null @@ -1,17 +0,0 @@ - - diff --git a/dashboard/src/main.ts b/dashboard/src/main.ts deleted file mode 100644 index 34a4d4f..0000000 --- a/dashboard/src/main.ts +++ /dev/null @@ -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') diff --git a/dashboard/src/router/index.ts b/dashboard/src/router/index.ts deleted file mode 100644 index ee53971..0000000 --- a/dashboard/src/router/index.ts +++ /dev/null @@ -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> = Object.assign({}, components, { - LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise, - LayoutMain: (() => import('/@/layout/aside.vue')) as unknown as () => Promise -}) - -export const allowRouter: Array = [ - { - 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 diff --git a/dtmcli/barrier.go b/dtmcli/barrier.go index 1fcf61f..0b136cb 100644 --- a/dtmcli/barrier.go +++ b/dtmcli/barrier.go @@ -20,11 +20,13 @@ type BarrierBusiFunc func(tx *sql.Tx) error // BranchBarrier every branch info type BranchBarrier struct { - TransType string - Gid string - BranchID string - Op string - BarrierID int + TransType string + Gid string + BranchID string + Op string + BarrierID int + DBType string // DBTypeMysql | DBTypePostgres + BarrierTableName 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, }[bb.Op] - originAffected, oerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, originOp, bid, bb.Op) - currentAffected, rerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, bb.Op, 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, bb.DBType, bb.BarrierTableName) 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. @@ -103,10 +105,12 @@ func (bb *BranchBarrier) CallWithDB(db *sql.DB, busiCall BarrierBusiFunc) error // QueryPrepared queries prepared data 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 if err == nil { 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) } if reason == dtmimp.OpRollback { diff --git a/dtmcli/dtmimp/db_special.go b/dtmcli/dtmimp/db_special.go index b4db7fc..2f20f17 100644 --- a/dtmcli/dtmimp/db_special.go +++ b/dtmcli/dtmimp/db_special.go @@ -75,8 +75,11 @@ func init() { } // GetDBSpecial get DBSpecial for currentDBType -func GetDBSpecial() DBSpecial { - return dbSpecials[currentDBType] +func GetDBSpecial(dbType string) DBSpecial { + if dbType == "" { + dbType = currentDBType + } + return dbSpecials[dbType] } // SetCurrentDBType set currentDBType diff --git a/dtmcli/dtmimp/db_special_test.go b/dtmcli/dtmimp/db_special_test.go index 3966cd2..74632a1 100644 --- a/dtmcli/dtmimp/db_special_test.go +++ b/dtmcli/dtmimp/db_special_test.go @@ -18,13 +18,13 @@ func TestDBSpecial(t *testing.T) { SetCurrentDBType("no-driver") })) SetCurrentDBType(DBTypeMysql) - sp := GetDBSpecial() + sp := GetDBSpecial(DBTypeMysql) assert.Equal(t, "? ?", sp.GetPlaceHoldSQL("? ?")) 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")) SetCurrentDBType(DBTypePostgres) - sp = GetDBSpecial() + sp = GetDBSpecial(DBTypePostgres) assert.Equal(t, "$1 $2", sp.GetPlaceHoldSQL("? ?")) 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")) diff --git a/dtmcli/dtmimp/trans_xa_base.go b/dtmcli/dtmimp/trans_xa_base.go index 50bb9c8..6eedb31 100644 --- a/dtmcli/dtmimp/trans_xa_base.go +++ b/dtmcli/dtmimp/trans_xa_base.go @@ -18,14 +18,14 @@ func XaHandlePhase2(gid string, dbConf DBConf, branchID string, op string) error return err } xaID := gid + "-" + branchID - _, err = DBExec(db, GetDBSpecial().GetXaSQL(op, xaID)) + _, err = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(op, xaID)) 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 err = nil } if op == OpRollback && err == nil { // 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 } @@ -39,20 +39,20 @@ func XaHandleLocalTrans(xa *TransBase, dbConf DBConf, cb func(*sql.DB) error) (r } defer func() { _ = db.Close() }() 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 }, func() error { return nil }) - _, rerr = DBExec(db, GetDBSpecial().GetXaSQL("start", xaBranch)) + _, rerr = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL("start", xaBranch)) if rerr != nil { return } 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 - _, 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 { rerr = cb(db) } diff --git a/dtmcli/dtmimp/utils.go b/dtmcli/dtmimp/utils.go index 7220b30..1e5607a 100644 --- a/dtmcli/dtmimp/utils.go +++ b/dtmcli/dtmimp/utils.go @@ -187,12 +187,12 @@ func XaDB(conf DBConf) (*sql.DB, error) { } // 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 == "" { return 0, nil } began := time.Now() - sql = GetDBSpecial().GetPlaceHoldSQL(sql) + sql = GetDBSpecial(dbType).GetPlaceHoldSQL(sql) r, rerr := db.Exec(sql, values...) used := time.Since(began) / time.Millisecond if rerr == nil { @@ -262,10 +262,16 @@ func EscapeGet(qs url.Values, key string) string { } // 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 == "" { return 0, nil } - sql := GetDBSpecial().GetInsertIgnoreTemplate(BarrierTableName+"(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)", "uniq_barrier") - return DBExec(tx, sql, transType, gid, branchID, op, barrierID, reason) + if dbType == "" { + 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) } diff --git a/dtmgrpc/type.go b/dtmgrpc/type.go index f92d830..14244e3 100644 --- a/dtmgrpc/type.go +++ b/dtmgrpc/type.go @@ -8,6 +8,7 @@ package dtmgrpc import ( context "context" + "fmt" "github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli/dtmimp" @@ -38,7 +39,8 @@ func GrpcError2DtmError(err error) error { if st.Message() == dtmcli.ResultOngoing { return dtmcli.ErrOngoing } - return dtmcli.ErrFailure + + return fmt.Errorf("%s. %w", st.Message(), dtmcli.ErrFailure) } else if ok && st.Code() == codes.FailedPrecondition { return dtmcli.ErrOngoing } diff --git a/dtmsvr/api.go b/dtmsvr/api.go index 809d2ee..831884a 100644 --- a/dtmsvr/api.go +++ b/dtmsvr/api.go @@ -15,6 +15,9 @@ import ( "github.com/dtm-labs/dtm/dtmsvr/storage" ) +// Version store the passin version for dtm server +var Version = "" + func svcSubmit(t *TransGlobal) interface{} { t.Status = dtmcli.StatusSubmitted branches, err := t.saveNew() diff --git a/dtmsvr/api_http.go b/dtmsvr/api_http.go index 2ca1da0..19aa7c6 100644 --- a/dtmsvr/api_http.go +++ b/dtmsvr/api_http.go @@ -19,6 +19,9 @@ import ( ) 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.POST("/api/dtmsvr/prepare", dtmutil.WrapHandler2(prepare)) 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} } -// resetCronTime rest 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 resetCronTime(c *gin.Context) interface{} { sTimeoutSecond := dtmimp.OrString(c.Query("timeout"), strconv.FormatInt(3*conf.TimeoutToFail, 10)) sLimit := dtmimp.OrString(c.Query("limit"), "100") diff --git a/dtmsvr/config/config.go b/dtmsvr/config/config.go index 0cb3aca..0ae4b41 100644 --- a/dtmsvr/config/config.go +++ b/dtmsvr/config/config.go @@ -6,7 +6,7 @@ import ( "github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli/logger" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) const ( @@ -53,14 +53,13 @@ type Store struct { Port int64 `yaml:"Port"` User string `yaml:"User"` Password string `yaml:"Password"` + Db string `yaml:"Db" default:"dtm"` MaxOpenConns int64 `yaml:"MaxOpenConns" default:"500"` MaxIdleConns int64 `yaml:"MaxIdleConns" default:"500"` ConnMaxLifeTime int64 `yaml:"ConnMaxLifeTime" default:"5"` 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. 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 @@ -76,10 +75,12 @@ func (s *Store) GetDBConf() dtmcli.DBConf { Port: s.Port, User: s.User, 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"` TransCronInterval int64 `yaml:"TransCronInterval" default:"3"` TimeoutToFail int64 `yaml:"TimeoutToFail" default:"35"` @@ -97,7 +98,7 @@ type configType struct { } // Config config -var Config = configType{} +var Config = Type{} // MustLoadConfig load config from env and file func MustLoadConfig(confFile string) { @@ -105,7 +106,7 @@ func MustLoadConfig(confFile string) { if confFile != "" { cont, err := ioutil.ReadFile(confFile) logger.FatalIfError(err) - err = yaml.UnmarshalStrict(cont, &Config) + err = yaml.Unmarshal(cont, &Config) logger.FatalIfError(err) } scont, err := json.MarshalIndent(&Config, "", " ") diff --git a/dtmsvr/config/config_utils.go b/dtmsvr/config/config_utils.go index f588eee..fa27cc7 100644 --- a/dtmsvr/config/config_utils.go +++ b/dtmsvr/config/config_utils.go @@ -58,7 +58,7 @@ func toUnderscoreUpper(key string) string { return strings.ToUpper(s2) } -func checkConfig(conf *configType) error { +func checkConfig(conf *Type) error { if conf.RetryInterval < 10 { return errors.New("RetryInterval should not be less than 10") } diff --git a/dtmsvr/entry/main.go b/dtmsvr/entry/main.go index 4fcc717..14d1247 100644 --- a/dtmsvr/entry/main.go +++ b/dtmsvr/entry/main.go @@ -14,13 +14,6 @@ import ( "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() { cmd := filepath.Base(os.Args[0]) 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.") // Main is the entry point of dtm server. -func Main(version *string) *gin.Engine { +func Main(version *string) (*gin.Engine, *config.Type) { flag.Parse() + if *version == "" { + *version = "v0.0.0-dev" + } + dtmsvr.Version = *version if flag.NArg() > 0 || *isHelp { usage() - return nil + return nil, nil } else if *isVersion { - ver(version) - return nil + fmt.Printf("dtm version: %s\n", *version) + return nil, nil } logger.Infof("dtm version is: %s", *version) config.MustLoadConfig(*confFile) @@ -58,5 +55,5 @@ func Main(version *string) *gin.Engine { registry.WaitStoreUp() app := dtmsvr.StartSvr() // start dtmsvr api go dtmsvr.CronExpiredTrans(-1) // start dtmsvr cron job - return app + return app, &config.Config } diff --git a/dtmsvr/storage/boltdb/boltdb.go b/dtmsvr/storage/boltdb/boltdb.go index b705a5b..eeedcaf 100644 --- a/dtmsvr/storage/boltdb/boltdb.go +++ b/dtmsvr/storage/boltdb/boltdb.go @@ -11,7 +11,6 @@ import ( "strings" "time" - "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/dtmsvr/storage" @@ -276,7 +275,7 @@ func (s *Store) ScanTransGlobalStores(position *string, limit int64) []storage.T globals := []storage.TransGlobalStore{} err := s.boltDb.View(func(t *bolt.Tx) error { 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 { continue } @@ -383,42 +382,42 @@ func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval // LockOneGlobalTrans finds GlobalTrans 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()) - next := time.Now().Add(time.Duration(s.retryInterval) * time.Second) err := s.boltDb.Update(func(t *bolt.Tx) error { cursor := t.Bucket(bucketIndex).Cursor() toDelete := [][]byte{} - for trans == nil || trans.Status == dtmcli.StatusSucceed || trans.Status == dtmcli.StatusFailed { - k, v := cursor.First() - if k == nil || string(k) > min { - return storage.ErrNotFound - } - trans = tGetGlobal(t, string(v)) + for k, v := cursor.First(); k != nil && string(k) <= min; k, v = cursor.Next() { toDelete = append(toDelete, k) + trans := tGetGlobal(t, string(v)) + if trans != nil && !trans.IsFinished() { + transo = trans + break + } } for _, k := range toDelete { err := t.Bucket(bucketIndex).Delete(k) dtmimp.E2P(err) } - trans.NextCronTime = &next - tPutGlobal(t, trans) - tPutIndex(t, next.Unix(), trans.Gid) + if transo != nil { + next := time.Now().Add(time.Duration(s.retryInterval) * time.Second) + transo.NextCronTime = &next + tPutGlobal(t, transo) + tPutIndex(t, next.Unix(), transo.Gid) + + } return nil }) - if err == storage.ErrNotFound { - return nil - } dtmimp.E2P(err) - return trans + return transo } -// ResetCronTime rest nextCronTime -// Prevent multiple backoff from causing NextCronTime to be too long -func (s *Store) ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { +// ResetCronTime reset nextCronTime +// unfinished transactions need to be retried as soon as possible after business downtime is recovered +func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { next := time.Now() 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 { cursor := t.Bucket(bucketIndex).Cursor() succeedCount = 0 diff --git a/dtmsvr/storage/redis/redis.go b/dtmsvr/storage/redis/redis.go index b529c25..d24d658 100644 --- a/dtmsvr/storage/redis/redis.go +++ b/dtmsvr/storage/redis/redis.go @@ -270,11 +270,11 @@ return gid } } -// ResetCronTime rest nextCronTime -// Prevent multiple backoff from causing NextCronTime to be too long -func (s *Store) ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { +// ResetCronTime reset nextCronTime +// unfinished transactions need to be retried as soon as possible after business downtime is recovered +func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { 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) lua := `-- ResetCronTime local r = redis.call('ZRANGEBYSCORE', KEYS[3], ARGV[3], '+inf', 'LIMIT', 0, ARGV[5]+1) diff --git a/dtmsvr/storage/registry/registry.go b/dtmsvr/storage/registry/registry.go index 35df62a..f09692f 100644 --- a/dtmsvr/storage/registry/registry.go +++ b/dtmsvr/storage/registry/registry.go @@ -49,7 +49,7 @@ func GetStore() storage.Store { // WaitStoreUp wait for db to go up func WaitStoreUp() { 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) } } diff --git a/dtmsvr/storage/sql/sql.go b/dtmsvr/storage/sql/sql.go index c15c7fc..0ad3565 100644 --- a/dtmsvr/storage/sql/sql.go +++ b/dtmsvr/storage/sql/sql.go @@ -137,61 +137,44 @@ func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval // LockOneGlobalTrans finds GlobalTrans func (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore { 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() - global := &storage.TransGlobalStore{} - dbr := db.Must().Model(global). - Where(whereTime + "and status in ('prepared', 'aborting', 'submitted')"). - Limit(1). - Select([]string{"owner", "next_cron_time"}). - Updates(&storage.TransGlobalStore{ - Owner: owner, - NextCronTime: dtmutil.GetNextTime(conf.RetryInterval), - }) - if dbr.RowsAffected == 0 { + nextCronTime := getTimeStr(int64(expireIn / time.Second)) + where := map[string]string{ + dtmimp.DBTypeMysql: fmt.Sprintf(`next_cron_time < '%s' and status in ('prepared', 'aborting', 'submitted') limit 1`, nextCronTime), + 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), + }[conf.Store.Driver] + + sql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s', owner='%s' WHERE %s`, + getTimeStr(0), + getTimeStr(conf.RetryInterval), + owner, + where) + affected, err := dtmimp.DBExec(conf.Store.Driver, db.ToSQLDB(), sql) + + dtmimp.PanicIf(err != nil, err) + if affected == 0 { return nil } + global := &storage.TransGlobalStore{} db.Must().Where("owner=?", owner).First(global) return global } -// ResetCronTime rest nextCronTime -// Prevent multiple backoff from causing NextCronTime to be too long -func (s *Store) ResetCronTime(timeout time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { - 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] - } - 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 - } - } +// ResetCronTime reset nextCronTime +// unfinished transactions need to be retried as soon as possible after business downtime is recovered +func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) { + nextCronTime := getTimeStr(int64(after / time.Second)) + where := map[string]string{ + dtmimp.DBTypeMysql: fmt.Sprintf(`next_cron_time > '%s' and status in ('prepared', 'aborting', 'submitted') limit %d`, nextCronTime, limit), + 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), + }[conf.Store.Driver] - 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 @@ -213,3 +196,7 @@ func wrapError(err error) error { dtmimp.E2P(err) return err } + +func getTimeStr(afterSecond int64) string { + return dtmutil.GetNextTime(afterSecond).Format("2006-01-02 15:04:05") +} diff --git a/dtmsvr/storage/store.go b/dtmsvr/storage/store.go index 391c4dd..bd2b400 100644 --- a/dtmsvr/storage/store.go +++ b/dtmsvr/storage/store.go @@ -30,5 +30,5 @@ type Store interface { ChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool) TouchCronTime(global *TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time) 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) } diff --git a/dtmsvr/storage/trans.go b/dtmsvr/storage/trans.go index 4f37122..dd95290 100644 --- a/dtmsvr/storage/trans.go +++ b/dtmsvr/storage/trans.go @@ -11,7 +11,6 @@ import ( "github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli/dtmimp" - "github.com/dtm-labs/dtm/dtmsvr/config" "github.com/dtm-labs/dtm/dtmutil" ) @@ -33,6 +32,7 @@ type TransGlobalStore struct { Protocol string `json:"protocol,omitempty"` FinishTime *time.Time `json:"finish_time,omitempty"` RollbackTime *time.Time `json:"rollback_time,omitempty"` + RollbackReason string `json:"rollback_reason,omitempty"` Options string `json:"options,omitempty"` CustomData string `json:"custom_data,omitempty"` NextCronInterval int64 `json:"next_cron_interval,omitempty"` @@ -45,13 +45,18 @@ type TransGlobalStore struct { // TableName TableName func (g *TransGlobalStore) TableName() string { - return config.Config.Store.TransGlobalTable + return "trans_global" } func (g *TransGlobalStore) String() string { 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 type TransBranchStore struct { dtmutil.ModelBase @@ -63,11 +68,12 @@ type TransBranchStore struct { Status string `json:"status,omitempty"` FinishTime *time.Time `json:"finish_time,omitempty"` RollbackTime *time.Time `json:"rollback_time,omitempty"` + Error error `json:"-" gorm:"-"` } // TableName TableName func (b *TransBranchStore) TableName() string { - return config.Config.Store.TransBranchOpTable + return "trans_branch_op" } func (b *TransBranchStore) String() string { diff --git a/dtmsvr/trans_status.go b/dtmsvr/trans_status.go index 6f6a344..fa1e51e 100644 --- a/dtmsvr/trans_status.go +++ b/dtmsvr/trans_status.go @@ -36,7 +36,22 @@ func (t *TransGlobal) touchCronTime(ctype cronType, delay uint64) { 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"} now := time.Now() if status == dtmcli.StatusSucceed { @@ -46,6 +61,10 @@ func (t *TransGlobal) changeStatus(status string) { t.RollbackTime = &now updates = append(updates, "rollback_time") } + if statusParams.rollbackReason != "" { + t.RollbackReason = statusParams.rollbackReason + updates = append(updates, "rollback_reason") + } t.UpdateTime = &now GetStore().ChangeGlobalStatus(&t.TransGlobalStore, status, updates, status == dtmcli.StatusSucceed || status == dtmcli.StatusFailed) 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 { return dtmcli.StatusSucceed, nil } 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 } else if errors.Is(err, dtmcli.ErrOngoing) { return "", dtmcli.ErrOngoing diff --git a/dtmsvr/trans_type_saga.go b/dtmsvr/trans_type_saga.go index 763d714..9117515 100644 --- a/dtmsvr/trans_type_saga.go +++ b/dtmsvr/trans_type_saga.go @@ -49,13 +49,14 @@ type branchResult struct { status string started bool op string + err error } func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error { // when saga tasks is fetched, it always need to process logger.Debugf("status: %s timeout: %t", t.Status, 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) @@ -73,6 +74,7 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error { } // resultStats var rsAToStart, rsAStarted, rsADone, rsAFailed, rsASucceed, rsCToStart, rsCDone, rsCSucceed int + var failureError error branchResults := make([]branchResult, n) // save the branch result for i := 0; i < n; i++ { b := branches[i] @@ -125,9 +127,9 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error { if x := recover(); x != nil { 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) { - 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) @@ -172,6 +174,7 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error { rsADone++ if r.status == dtmcli.StatusFailed { rsAFailed++ + failureError = r.err } else if r.status == dtmcli.StatusSucceed { rsASucceed++ } @@ -219,8 +222,11 @@ func (t *transSagaProcessor) ProcessOnce(branches []TransBranch) error { t.changeStatus(dtmcli.StatusSucceed) return nil } - if t.Status == dtmcli.StatusSubmitted && (rsAFailed > 0 || t.isTimeout()) { - t.changeStatus(dtmcli.StatusAborting) + if t.Status == dtmcli.StatusSubmitted && rsAFailed > 0 { + 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 { prepareToCompensate() diff --git a/dtmsvr/trans_type_tcc.go b/dtmsvr/trans_type_tcc.go index 5146226..11f23b7 100644 --- a/dtmsvr/trans_type_tcc.go +++ b/dtmsvr/trans_type_tcc.go @@ -1,6 +1,8 @@ package dtmsvr import ( + "fmt" + "github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli/dtmimp" "github.com/dtm-labs/dtm/dtmcli/logger" @@ -23,7 +25,7 @@ func (t *transTccProcessor) ProcessOnce(branches []TransBranch) error { return nil } 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) for current := len(branches) - 1; current >= 0; current-- { diff --git a/dtmsvr/trans_type_xa.go b/dtmsvr/trans_type_xa.go index d4ca3b3..c74f188 100644 --- a/dtmsvr/trans_type_xa.go +++ b/dtmsvr/trans_type_xa.go @@ -1,6 +1,8 @@ package dtmsvr import ( + "fmt" + "github.com/dtm-labs/dtm/dtmcli" "github.com/dtm-labs/dtm/dtmcli/dtmimp" ) @@ -22,7 +24,7 @@ func (t *transXaProcessor) ProcessOnce(branches []TransBranch) error { return nil } 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) for i, branch := range branches { diff --git a/dtmutil/db.go b/dtmutil/db.go index 1862870..043f7f9 100644 --- a/dtmutil/db.go +++ b/dtmutil/db.go @@ -100,7 +100,7 @@ func DbGet(conf dtmcli.DBConf, ops ...func(*gorm.DB)) *DB { dsn := dtmimp.GetDsn(conf) db, ok := dbs.Load(dsn) 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{ SkipDefaultTransaction: true, }) diff --git a/dtmutil/utils.go b/dtmutil/utils.go index 06b39a7..e7bbeba 100644 --- a/dtmutil/utils.go +++ b/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 func WrapHandler2(fn func(*gin.Context) interface{}) gin.HandlerFunc { 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")) { continue } - _, err = dtmimp.DBExec(con, s) + _, err = dtmimp.DBExec(conf.Driver, con, s) logger.FatalIfError(err) logger.Infof("sql scripts finished: %s", s) } diff --git a/go.mod b/go.mod index 26ebd15..a45dc32 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,13 @@ require ( bou.ke/monkey v1.0.2 github.com/BurntSushi/toml v0.4.1 // indirect 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-kratos v0.0.8 github.com/dtm-labs/dtmdriver-polaris v0.0.4 github.com/dtm-labs/dtmdriver-protocol1 v0.0.1 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-resty/resty/v2 v2.7.0 github.com/go-sql-driver/mysql v1.6.0 @@ -22,14 +23,16 @@ require ( github.com/onsi/gomega v1.16.0 github.com/prometheus/client_golang v1.11.0 github.com/stretchr/testify v1.7.1 + github.com/ugorji/go v1.2.7 // indirect 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/zap v1.21.0 - golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect - google.golang.org/grpc v1.45.0 + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + google.golang.org/grpc v1.46.2 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/postgres v1.2.1 gorm.io/gorm v1.22.2 diff --git a/go.sum b/go.sum index 0535ea6..98d85de 100644 --- a/go.sum +++ b/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/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/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.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/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= @@ -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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= 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-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-20211001041855-01bcc9b48dfe/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/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.3 h1:9iAtvXKR3lJXQ7dvS87e4xdtmqkzN+ofek+CF9AvUSY= 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.3/go.mod h1:B5FqzfvzGe2AcB8MpAP34eSLszkCd6hm+t5KJN2Fu/o= -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-gozero v0.0.5 h1:cMBkURDRPcb1Y6/kY+x/yxYA8KWMG+fkK6b+cs4WXKo= +github.com/dtm-labs/dtmdriver-gozero v0.0.5/go.mod h1:U4nIisQ5SBZrajp7ky1Bi0eBxPiGqVy+tOs47beobY4= 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-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.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.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/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.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 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/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 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.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/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= @@ -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/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/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/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/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.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/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 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/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/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/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.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= 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.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/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/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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.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.1.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-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/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/go.mod h1:UHOtQNQY/qpk2dhg6gDq8u5+/CEIc3+lWmrmxEzX0/g= 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/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.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/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 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.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 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/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 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/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/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/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.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/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 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/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.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.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/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/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= 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.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA= 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.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.9.0 h1:f3aLGJvQmBl8d9S40IL+jEyBC6hfLPbJjv9t5hEM9ck= +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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 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.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +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/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/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.6.3 h1:IqN4L+5b0mPNjdXIiZ90Ni4Bl5BRkDQywePLWemd9bc= -go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +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.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= @@ -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-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-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-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-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-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= 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-20190226205417-e64efc72b421/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-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-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-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-20180830151530-49385e6e1522/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-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-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-20210303074136-134d130e1a04/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-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-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-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-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-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-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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-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-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-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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 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.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.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= 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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 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.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/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 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/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-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.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/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= gorm.io/driver/postgres v1.2.1 h1:JDQKnF7MC51dgL09Vbydc5kl83KkVDlcXfSPJ+xhh68= diff --git a/helper/.goreleaser.yml b/helper/.goreleaser.yml index 83e0158..02e6060 100644 --- a/helper/.goreleaser.yml +++ b/helper/.goreleaser.yml @@ -12,9 +12,9 @@ builds: dir: . main: main.go ldflags: - - -s -w -X main.Version={{.Version}} + - -s -w -X main.Version=v{{.Version}} - id: dtm_arm64 - env: [ CGO_ENABLED=0 ] + env: [CGO_ENABLED=0] goos: - darwin goarch: @@ -22,4 +22,4 @@ builds: dir: . main: main.go ldflags: - - -s -w -X main.Version={{.Version}} + - -s -w -X main.Version=v{{.Version}} diff --git a/helper/Dockerfile-release b/helper/Dockerfile-release index 45127c2..568de9a 100644 --- a/helper/Dockerfile-release +++ b/helper/Dockerfile-release @@ -1,5 +1,11 @@ # 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 TARGETOS ARG RELEASE_VERSION @@ -7,9 +13,10 @@ WORKDIR /app/dtm # RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct EXPOSE 8080 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" FROM --platform=$TARGETPLATFORM alpine -COPY --from=builder /app/dtm/dtm /app/dtm/ +COPY --from=builder2 /app/dtm/dtm /app/dtm/ WORKDIR /app/dtm ENTRYPOINT ["/app/dtm/dtm"] diff --git a/helper/README-cn.md b/helper/README-cn.md index f7a9bfe..2153ae3 100644 --- a/helper/README-cn.md +++ b/helper/README-cn.md @@ -9,14 +9,7 @@ # 跨语言分布式事务管理器 -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)等框架)中跨服务更新数据保证一致性 - -他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供跨语言,跨存储引擎组合事务的强大功能: - +DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方式,极大的降低了分布式事务的使用门槛,改变了“能不用分布式事务就不用”的行业现状,优雅的解决了服务间的数据一致性问题。 ## 谁在使用DTM(仅列出部分) [Tencent 腾讯](https://dtm.pub/other/using.html#tencent) @@ -25,39 +18,26 @@ DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方 [Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad) -## 亮点 - -* 极易上手 - - 零配置启动服务,提供非常简单的HTTP接口,极大降低上手分布式事务的难度,新手也能快速接入 -* 跨语言 - - 可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用。 -* 使用简单 - - 开发者不再担心悬挂、空补偿、幂等各类问题,首创子事务屏障技术代为处理 -* 易部署、易扩展 - - 依赖mysql/redis,部署简单,易集群化,易水平扩展 -* 多种分布式事务协议支持 - - TCC、SAGA、XA、二阶段消息,一站式解决所有分布式事务问题 - -## 与其他框架对比 - -非Java语言类的,暂未看到除dtm之外的成熟框架,因此这里将DTM和Java中最成熟的Seata对比: +[更多](https://dtm.pub/other/using.html) -| 特性| DTM | SEATA |备注| -|:-----:|:----:|:----:|:----:| -|[支持语言](https://dtm.pub/other/opensource.html#lang) |Go、c#、Java、python、php...|Java|dtm可轻松接入一门新语言| -|[存储引擎](https://dtm.pub/other/opensource.html#store) |支持数据库、Redis、Mongo等|数据库|| -|[异常处理](https://dtm.pub/other/opensource.html#exception)| 子事务屏障自动处理 |手动处理 |dtm解决了幂等、悬挂、空补偿| -|[SAGA事务](https://dtm.pub/other/opensource.html#saga) |极简易用 |复杂状态机 || -|[二阶段消息](https://dtm.pub/other/opensource.html#msg)|||最简消息最终一致性架构| -|[TCC事务](https://dtm.pub/other/opensource.html#tcc)| ||| -|[XA事务](https://dtm.pub/other/opensource.html#xa)|||| -|[AT事务](https://dtm.pub/other/opensource.html#at)|建议使用XA||AT与XA类似,但有脏回滚| -|[单服务多数据源](https://dtm.pub/other/opensource.html#multidb)|||| -|[通信协议](https://dtm.pub/other/opensource.html#protocol)|HTTP、gRPC、go-zero|dubbo等协议|dtm对云原生更加友好| +如果贵公司也已使用 dtm,欢迎在 [登记地址](https://github.com/dtm-labs/dtm/issues/7) 登记,仅仅为了推广,不做其它用途。 -从上面对比的特性来看,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) @@ -65,12 +45,6 @@ DTM是一款变革性的分布式事务框架,提供了傻瓜式的使用方 ## [各语言客户端及示例](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),里面有相关的快速开始示例 @@ -100,7 +74,7 @@ go run main.go req := &gin.H{"amount": 30} // 微服务的载荷 // DtmServer为DTM服务的地址,是一个url 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" Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). // 添加一个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官方公众号:分布式事务,大量干货分享,以及dtm的最新消息 -### 交流群 -请加 yedf2008 好友或者扫码加好友,验证回复 dtm 按照指引进群 +### 微信交流群 + +如果您希望更快的获得反馈,或者更多的了解其他用户在使用过程中的各种反馈,欢迎加入我们的微信交流群 + +请加作者的微信 yedf2008 好友或者扫码加好友,备注 `dtm` 按照指引进群 ![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) diff --git a/helper/README-en.md b/helper/README-en.md index 5e49007..740ae92 100644 --- a/helper/README-en.md +++ b/helper/README-en.md @@ -17,11 +17,13 @@ DTM is a distributed transaction framework which provides cross-service eventual ## 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) @@ -54,7 +56,7 @@ go run main.go DtmServer := "http://localhost:36789/api/dtmsvr" req := &gin.H{"amount": 30} // micro-service payload // 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(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCom" diff --git a/helper/bench/svr/http.go b/helper/bench/svr/http.go index 0c61c5a..5a8b883 100644 --- a/helper/bench/svr/http.go +++ b/helper/bench/svr/http.go @@ -53,7 +53,7 @@ func reloadData() { db := pdbGet() 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 { - _, err := dtmimp.DBExec(db, fmt.Sprintf("truncate %s", t)) + _, err := dtmimp.DBExec(busi.BusiConf.Driver, db, fmt.Sprintf("truncate %s", t)) logger.FatalIfError(err) } s := "insert ignore into dtm_busi.user_account(user_id, balance) values " @@ -61,7 +61,7 @@ func reloadData() { for i := 1; i <= total; 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.Debugf("%d users inserted. used: %dms", total, time.Since(began).Milliseconds()) } @@ -73,11 +73,11 @@ var sqls = 1 // PrepareBenchDB prepares db data for bench func PrepareBenchDB() { 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) - _, 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) - _, 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, user_id INT(11) 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()) f := func(tx *sql.Tx) error { 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"))) 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) } return nil @@ -122,7 +122,7 @@ func qsAdjustBalance(uid int, amount int, c *gin.Context) error { // nolint: unp if strings.Contains(mode, "barrier") { barrier, err := dtmcli.BarrierFromQuery(c.Request.URL.Query()) logger.FatalIfError(err) - err = barrier.Call(txGet(), f) + err = barrier.CallWithDB(pdbGet(), f) logger.FatalIfError(err) } else { tx := txGet() diff --git a/helper/compose.cloud.yml b/helper/compose.cloud.yml deleted file mode 100644 index 641adf9..0000000 --- a/helper/compose.cloud.yml +++ /dev/null @@ -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' diff --git a/helper/compose.mysql.yml b/helper/compose.mysql.yml deleted file mode 100644 index 0c5ec71..0000000 --- a/helper/compose.mysql.yml +++ /dev/null @@ -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' diff --git a/helper/compose.postgres.yml b/helper/compose.postgres.yml deleted file mode 100644 index dc80e61..0000000 --- a/helper/compose.postgres.yml +++ /dev/null @@ -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' diff --git a/helper/compose.store.yml b/helper/compose.store.yml index 4053807..0fb4707 100644 --- a/helper/compose.store.yml +++ b/helper/compose.store.yml @@ -22,6 +22,7 @@ services: - /etc/timezone:/etc/timezone:ro environment: POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: dtm ports: - '5432:5432' diff --git a/helper/golint.sh b/helper/golint.sh index 6c4b568..71f1817 100644 --- a/helper/golint.sh +++ b/helper/golint.sh @@ -1,4 +1,4 @@ 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 diff --git a/helper/test-cover.sh b/helper/test-cover.sh index 13bd654..e490032 100755 --- a/helper/test-cover.sh +++ b/helper/test-cover.sh @@ -1,6 +1,6 @@ set -x 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 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 diff --git a/main.go b/main.go index 8b8a134..2dd51c2 100644 --- a/main.go +++ b/main.go @@ -7,12 +7,20 @@ package main import ( + "embed" "fmt" + "io/fs" + "io/ioutil" "net/http" "net/http/httputil" "net/url" + "os" + "os/signal" + "strings" + "syscall" "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/microservices" "github.com/gin-gonic/gin" @@ -22,23 +30,61 @@ import ( var Version string func main() { - app := entry.Main(&Version) + app, conf := entry.Main(&Version) if app != nil { - addDashboard(app) - select {} + addAdmin(app, conf) + 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) - app.GET("/@vite/*name", proxyDashboard) - app.GET("/node_modules/*name", proxyDashboard) - app.GET("/src/*name", proxyDashboard) - app.GET("/@id/*name", proxyDashboard) + +//go:embed admin/dist +var admin embed.FS + +var target = "" + +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.Scheme = "http" u.Host = target @@ -49,7 +95,8 @@ func proxyDashboard(c *gin.Context) { ret := fmt.Sprintf("http proxy error %v", err) _, _ = 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) } diff --git a/sqls/dtmsvr.storage.mysql.sql b/sqls/dtmsvr.storage.mysql.sql index 283bb75..50e4f9a 100644 --- a/sqls/dtmsvr.storage.mysql.sql +++ b/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', `owner` varchar(128) not null default '' comment 'who is locking this trans', `ext_data` TEXT comment 'extended data for this trans', + `rollback_reason` varchar(1024) DEFAULT '' COMMENT 'rollback reason for transaction', PRIMARY KEY (`id`), UNIQUE KEY `gid` (`gid`), key `owner`(`owner`), @@ -41,3 +42,15 @@ CREATE TABLE IF NOT EXISTS dtm.trans_branch_op ( PRIMARY KEY (`id`), UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`) ) 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; diff --git a/sqls/dtmsvr.storage.postgres.sql b/sqls/dtmsvr.storage.postgres.sql index 32af57f..898de34 100644 --- a/sqls/dtmsvr.storage.postgres.sql +++ b/sqls/dtmsvr.storage.postgres.sql @@ -1,11 +1,8 @@ -CREATE SCHEMA if not EXISTS dtm -/* SQLINES DEMO *** RACTER SET utf8mb4 */ -; -drop table IF EXISTS dtm.trans_global; --- SQLINES LICENSE FOR EVALUATION USE ONLY -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'), +drop table IF EXISTS trans_global; + +CREATE SEQUENCE if not EXISTS trans_global_seq; +CREATE TABLE if not EXISTS trans_global ( + id bigint NOT NULL DEFAULT NEXTVAL ('trans_global_seq'), gid varchar(128) NOT NULL, trans_type 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, owner varchar(128) not null default '', ext_data text, + rollback_reason varchar(1024) DEFAULT '', PRIMARY KEY (id), CONSTRAINT gid UNIQUE (gid) ); -create index if not EXISTS owner on dtm.trans_global(owner); -create index if not EXISTS status_next_cron_time on dtm.trans_global (status, next_cron_time); -drop table IF EXISTS dtm.trans_branch_op; --- SQLINES LICENSE FOR EVALUATION USE ONLY -CREATE SEQUENCE if not EXISTS dtm.trans_branch_op_seq; -CREATE TABLE IF NOT EXISTS dtm.trans_branch_op ( - id bigint NOT NULL DEFAULT NEXTVAL ('dtm.trans_branch_op_seq'), +create index if not EXISTS owner on trans_global(owner); +create index if not EXISTS status_next_cron_time on trans_global (status, next_cron_time); +drop table IF EXISTS trans_branch_op; + +CREATE SEQUENCE if not EXISTS trans_branch_op_seq; +CREATE TABLE IF NOT EXISTS trans_branch_op ( + id bigint NOT NULL DEFAULT NEXTVAL ('trans_branch_op_seq'), gid varchar(128) NOT NULL, url varchar(1024) NOT NULL, data TEXT, @@ -44,4 +42,4 @@ CREATE TABLE IF NOT EXISTS dtm.trans_branch_op ( update_time timestamp(0) with time zone DEFAULT NULL, PRIMARY KEY (id), CONSTRAINT gid_branch_uniq UNIQUE (gid, branch_id, op) -); \ No newline at end of file +); diff --git a/sqls/dtmsvr.storage.tdsql.sql b/sqls/dtmsvr.storage.tdsql.sql index ded809f..a24992c 100644 --- a/sqls/dtmsvr.storage.tdsql.sql +++ b/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', `owner` varchar(128) not null default '' comment 'who is locking 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`), UNIQUE KEY `id` (`id`,`gid`), UNIQUE KEY `gid` (`gid`), diff --git a/test/api_test.go b/test/api_test.go index ff3d5d6..e63fc27 100644 --- a/test/api_test.go +++ b/test/api_test.go @@ -18,6 +18,12 @@ import ( "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) { gid := dtmimp.GetFuncName() err := genMsg(gid).Submit() diff --git a/test/base_test.go b/test/base_test.go index 68ea359..3fbd05c 100644 --- a/test/base_test.go +++ b/test/base_test.go @@ -26,7 +26,7 @@ type BarrierModel struct { } // TableName gorm table name -func (BarrierModel) TableName() string { return "dtm_barrier.barrier" } +func (BarrierModel) TableName() string { return dtmimp.BarrierTableName } func TestBaseSqlDB(t *testing.T) { asserts := assert.New(t) @@ -38,7 +38,7 @@ func TestBaseSqlDB(t *testing.T) { Op: dtmimp.OpAction, 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() asserts.Nil(err) err = barrier.Call(tx, func(tx *sql.Tx) error { diff --git a/test/busi/barrier.go b/test/busi/barrier.go index 7179e14..b80cb55 100644 --- a/test/busi/barrier.go +++ b/test/busi/barrier.go @@ -22,49 +22,49 @@ func init() { setupFuncs["BarrierSetup"] = func(app *gin.Engine) { app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 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) }) })) app.POST(BusiAPI+"/SagaBTransInCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 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, "") }) })) app.POST(BusiAPI+"/SagaB2TransIn", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 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) }) if err != nil { 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) }) })) app.POST(BusiAPI+"/SagaB2TransInCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 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, "") }) if err != nil { 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, "") }) })) app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 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) }) })) app.POST(BusiAPI+"/SagaBTransOutCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 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, "") }) })) @@ -82,17 +82,17 @@ func init() { if 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) }) })) 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) }) })) 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) }) })) @@ -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) }) })) @@ -178,7 +178,7 @@ func init() { if reqFrom(c).Store == Redis || reqFrom(c).Store == Mongo { 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) }) })) @@ -198,7 +198,7 @@ func TccBarrierTransOutCancel(c *gin.Context) interface{} { 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) }) } @@ -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) { 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) }) } func (s *busiServer) TransInRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) { 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, "") }) } func (s *busiServer) TransOutRevertBSaga(ctx context.Context, in *BusiReq) (*emptypb.Empty, error) { 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, "") }) } diff --git a/test/busi/base_http.go b/test/busi/base_http.go index e2609bf..c2bf076 100644 --- a/test/busi/base_http.go +++ b/test/busi/base_http.go @@ -69,7 +69,8 @@ func BaseAppStartup() *gin.Engine { } logger.Debugf("Starting busi at: %d", BusiPort) go func() { - _ = app.Run(fmt.Sprintf(":%d", BusiPort)) + err := app.Run(fmt.Sprintf(":%d", BusiPort)) + dtmimp.FatalIfError(err) }() return app } @@ -140,7 +141,7 @@ func BaseAddRoute(app *gin.Engine) { })) 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 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{} { diff --git a/test/busi/base_types.go b/test/busi/base_types.go index 541c2cb..0d4171d 100644 --- a/test/busi/base_types.go +++ b/test/busi/base_types.go @@ -142,6 +142,7 @@ type mainSwitchType struct { QueryPreparedResult AutoEmptyString NextResult AutoEmptyString JrpcResult AutoEmptyString + FailureReason AutoEmptyString } // MainSwitch controls busi success or fail diff --git a/test/busi/busi.go b/test/busi/busi.go index c4026ed..81c9c9e 100644 --- a/test/busi/busi.go +++ b/test/busi/busi.go @@ -34,7 +34,7 @@ func handleGrpcBusiness(in *BusiReq, result1 string, result2 string, busi string if res == dtmcli.ResultSuccess { return nil } 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 { 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" { 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) } @@ -66,7 +69,7 @@ func sagaGrpcAdjustBalance(db dtmcli.DB, uid int, amount int64, result string) e if result == dtmcli.ResultFailure { 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 } @@ -75,7 +78,7 @@ func SagaAdjustBalance(db dtmcli.DB, uid int, amount int, result string) error { if strings.Contains(result, dtmcli.ResultFailure) { 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 } @@ -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 nil - } 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+? - where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount) + affected, err := dtmimp.DBExec(BusiConf.Driver, db, `update dtm_busi.user_account + set trading_balance=trading_balance+? + where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount) if err == nil && affected == 0 { 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 { - affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account set trading_balance=trading_balance-?, - balance=balance+? where user_id=?`, amount, amount, uid) + affected, err := dtmimp.DBExec(BusiConf.Driver, db, `update dtm_busi.user_account + set trading_balance=trading_balance-?, + balance=balance+? where user_id=?`, amount, amount, uid) if err == nil && affected == 0 { return fmt.Errorf("update user_account 0 rows") } diff --git a/test/busi/quick_start.go b/test/busi/quick_start.go index 6d57af5..2f33d72 100644 --- a/test/busi/quick_start.go +++ b/test/busi/quick_start.go @@ -7,6 +7,7 @@ import ( "github.com/dtm-labs/dtm/dtmcli" "github.com/gin-gonic/gin" + "github.com/lithammer/shortuuid/v3" ) // busi address @@ -59,7 +60,7 @@ const dtmServer = "http://localhost:36789/api/dtmsvr" func QsFireRequest() string { req := &gin.H{"amount": 30} // load of micro-service // 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(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req). // add a TransIn subtraction, forward operation with url: qsBusi+"/TransIn", reverse compensation operation with url: qsBusi+"/TransInCompensate" diff --git a/test/common_test.go b/test/common_test.go index 2a9b917..a28a144 100644 --- a/test/common_test.go +++ b/test/common_test.go @@ -33,12 +33,12 @@ func testSql(t *testing.T) { func testDbAlone(t *testing.T) { db, err := dtmimp.StandaloneDB(conf.Store.GetDBConf()) assert.Nil(t, err) - _, err = dtmimp.DBExec(db, "select 1") + _, err = dtmimp.DBExec(conf.Store.Driver, db, "select 1") assert.Equal(t, nil, err) - _, err = dtmimp.DBExec(db, "") + _, err = dtmimp.DBExec(conf.Store.Driver, db, "") assert.Equal(t, nil, err) db.Close() - _, err = dtmimp.DBExec(db, "select 1") + _, err = dtmimp.DBExec(conf.Store.Driver, db, "select 1") assert.NotEqual(t, nil, err) } diff --git a/test/dtmsvr_test.go b/test/dtmsvr_test.go index 345bebe..273fe36 100644 --- a/test/dtmsvr_test.go +++ b/test/dtmsvr_test.go @@ -26,6 +26,10 @@ func getTransStatus(gid string) string { return dtmsvr.GetTransGlobal(gid).Status } +func getTrans(gid string) *dtmsvr.TransGlobal { + return dtmsvr.GetTransGlobal(gid) +} + func getBranchesStatus(gid string) []string { branches := dtmsvr.GetStore().FindBranches(gid) status := []string{} diff --git a/test/main_test.go b/test/main_test.go index 4f17d73..a9f48d8 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -30,7 +30,6 @@ func exitIf(code int) { func TestMain(m *testing.M) { config.MustLoadConfig("") logger.InitLog("debug") - dtmcli.SetCurrentDBType(busi.BusiConf.Driver) dtmsvr.TransProcessedTestChan = make(chan string, 1) dtmsvr.NowForwardDuration = 0 * time.Second dtmsvr.CronForwardDuration = 180 * time.Second @@ -41,24 +40,31 @@ func TestMain(m *testing.M) { dtmcli.GetRestyClient().OnAfterResponse(func(c *resty.Client, resp *resty.Response) error { return nil }) tenv := os.Getenv("TEST_STORE") + conf.Store.Host = "localhost" + conf.Store.Driver = tenv if tenv == "boltdb" { - conf.Store.Driver = "boltdb" - } else if tenv == "mysql" { - conf.Store.Driver = "mysql" - conf.Store.Host = "localhost" + } else if tenv == config.Mysql { conf.Store.Port = 3306 conf.Store.User = "root" conf.Store.Password = "" - } else { - conf.Store.Driver = "redis" - conf.Store.Host = "localhost" + } else if tenv == config.Postgres { + conf.Store.Port = 5432 + conf.Store.User = "postgres" + conf.Store.Password = "mysecretpassword" + } else if tenv == config.Redis { conf.Store.User = "" conf.Store.Password = "" conf.Store.Port = 6379 } + conf.Store.Db = "" registry.WaitStoreUp() dtmsvr.PopulateDB(false) + conf.Store.Db = "dtm" // after populateDB, set current db to dtm + if tenv == "postgres" { + busi.BusiConf = conf.Store.GetDBConf() + dtmcli.SetCurrentDBType(tenv) + } go dtmsvr.StartSvr() busi.PopulateDB(false) diff --git a/test/saga_grpc_test.go b/test/saga_grpc_test.go index 94f1732..9a98d06 100644 --- a/test/saga_grpc_test.go +++ b/test/saga_grpc_test.go @@ -28,6 +28,7 @@ func TestSagaGrpcNormal(t *testing.T) { func TestSagaGrpcRollback(t *testing.T) { gid := dtmimp.GetFuncName() saga := genSagaGrpc(gid, false, true) + busi.MainSwitch.FailureReason.SetOnce("Insufficient balance") busi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing) saga.Submit() waitTransProcessed(saga.Gid) @@ -35,6 +36,7 @@ func TestSagaGrpcRollback(t *testing.T) { cronTransOnce(t, gid) assert.Equal(t, StatusFailed, getTransStatus(saga.Gid)) assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid)) + assert.Equal(t, "url:localhost:58081/busi.Busi/TransIn return failed: reason:Insufficient balance. FAILURE", getTrans(saga.Gid).RollbackReason) } func TestSagaGrpcCurrent(t *testing.T) { diff --git a/test/saga_options_test.go b/test/saga_options_test.go index 6134ee7..557ab1d 100644 --- a/test/saga_options_test.go +++ b/test/saga_options_test.go @@ -56,6 +56,8 @@ func TestSagaOptionsTimeout(t *testing.T) { cronTransOnceForwardNow(t, gid, 3600) assert.Equal(t, StatusFailed, getTransStatus(saga.Gid)) assert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid)) + assert.Regexp(t, `^Timeout after \d+ seconds$`, getTrans(gid).RollbackReason) + } func TestSagaGlobalTransWithRequestTimeout(t *testing.T) { diff --git a/test/saga_test.go b/test/saga_test.go index d7b9ad7..f536a4c 100644 --- a/test/saga_test.go +++ b/test/saga_test.go @@ -22,15 +22,18 @@ func TestSagaNormal(t *testing.T) { waitTransProcessed(saga.Gid) assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid)) assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid)) + assert.Equal(t, "", getTrans(saga.Gid).RollbackReason) } func TestSagaRollback(t *testing.T) { saga := genSaga(dtmimp.GetFuncName(), false, true) + busi.MainSwitch.FailureReason.SetOnce("Insufficient balance") err := saga.Submit() assert.Nil(t, err) waitTransProcessed(saga.Gid) assert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid)) assert.Equal(t, StatusFailed, getTransStatus(saga.Gid)) + assert.Equal(t, "url:http://localhost:8081/api/busi/TransIn return failed: {\"error\":\"reason:Insufficient balance. FAILURE\"}. FAILURE", getTrans(saga.Gid).RollbackReason) } func TestSagaOngoingSucceed(t *testing.T) { @@ -44,6 +47,7 @@ func TestSagaOngoingSucceed(t *testing.T) { cronTransOnce(t, gid) assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid)) assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid)) + assert.Equal(t, "", getTrans(saga.Gid).RollbackReason) } func TestSagaFailed(t *testing.T) { diff --git a/test/store_test.go b/test/store_test.go index b9bb31e..4cc3c00 100644 --- a/test/store_test.go +++ b/test/store_test.go @@ -100,73 +100,73 @@ func TestStoreResetCronTime(t *testing.T) { }) } -func testStoreResetCronTime(t *testing.T, funcName string, restCronHandler func(expire int64, limit int64) (int64, bool, error)) { +func testStoreResetCronTime(t *testing.T, funcName string, resetCronHandler func(expire int64, limit int64) (int64, bool, error)) { s := registry.GetStore() - var restTimeTimeout, lockExpireIn, limit, i int64 - restTimeTimeout = 100 //The time that will be ResetCronTime - lockExpireIn = 2 //The time that will be LockOneGlobalTrans - limit = 10 // rest limit + var afterSeconds, lockExpireIn, limit, i int64 + afterSeconds = 100 + lockExpireIn = 2 + limit = 10 // Will be reset for i = 0; i < limit; i++ { gid := funcName + fmt.Sprintf("%d", i) - _, _ = initTransGlobalByNextCronTime(gid, time.Now().Add(time.Duration(restTimeTimeout+10)*time.Second)) + _, _ = initTransGlobalByNextCronTime(gid, time.Now().Add(time.Duration(afterSeconds+10)*time.Second)) } // Will not be reset gid := funcName + fmt.Sprintf("%d", 10) - _, _ = initTransGlobalByNextCronTime(gid, time.Now().Add(time.Duration(restTimeTimeout-10)*time.Second)) + _, _ = initTransGlobalByNextCronTime(gid, time.Now().Add(time.Duration(afterSeconds-10)*time.Second)) - // Not Fount + // Not Found g := s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.Nil(t, g) - // Rest limit-1 count - succeedCount, hasRemaining, err := restCronHandler(restTimeTimeout, limit-1) + // Reset limit-1 count + succeedCount, hasRemaining, err := resetCronHandler(afterSeconds, limit-1) assert.Equal(t, hasRemaining, true) assert.Equal(t, succeedCount, limit-1) assert.Nil(t, err) - // Fount limit-1 count + // Found limit-1 count for i = 0; i < limit-1; i++ { g = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.NotNil(t, g) s.ChangeGlobalStatus(g, "succeed", []string{}, true) } - // Not Fount + // Not Found g = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.Nil(t, g) - // Rest 1 count - succeedCount, hasRemaining, err = restCronHandler(restTimeTimeout, limit) + // Reset 1 count + succeedCount, hasRemaining, err = resetCronHandler(afterSeconds, limit) assert.Equal(t, hasRemaining, false) assert.Equal(t, succeedCount, int64(1)) assert.Nil(t, err) - // Fount 1 count + // Found 1 count g = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.NotNil(t, g) s.ChangeGlobalStatus(g, "succeed", []string{}, true) - // Not Fount + // Not Found g = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.Nil(t, g) - // reduce the restTimeTimeout, Rest 1 count - succeedCount, hasRemaining, err = restCronHandler(restTimeTimeout-12, limit) + // reduce the resetTimeTimeout, Reset 1 count + succeedCount, hasRemaining, err = resetCronHandler(afterSeconds-12, limit) assert.Equal(t, hasRemaining, false) assert.Equal(t, succeedCount, int64(1)) assert.Nil(t, err) - // Fount 1 count + // Found 1 count g = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.NotNil(t, g) s.ChangeGlobalStatus(g, "succeed", []string{}, true) - // Not Fount + // Not Found g = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second) assert.Nil(t, g) - // Not Fount - succeedCount, hasRemaining, err = restCronHandler(restTimeTimeout-12, limit) + // Not Found + succeedCount, hasRemaining, err = resetCronHandler(afterSeconds-12, limit) assert.Equal(t, hasRemaining, false) assert.Equal(t, succeedCount, int64(0)) assert.Nil(t, err) diff --git a/test/tcc_test.go b/test/tcc_test.go index 60fccd2..f4f229d 100644 --- a/test/tcc_test.go +++ b/test/tcc_test.go @@ -67,6 +67,7 @@ func TestTccTimeout(t *testing.T) { }) assert.Error(t, err) assert.Equal(t, StatusFailed, getTransStatus(gid)) + assert.Regexp(t, `^Timeout after \d+ seconds$`, getTrans(gid).RollbackReason) assert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid)) } diff --git a/test/xa_cover_test.go b/test/xa_cover_test.go index 6836582..8c602ae 100644 --- a/test/xa_cover_test.go +++ b/test/xa_cover_test.go @@ -39,6 +39,9 @@ func TestXaCoverDTMError(t *testing.T) { } func TestXaCoverGidError(t *testing.T) { + if dtmimp.GetCurrentDBType() != dtmimp.DBTypeMysql { + return + } gid := dtmimp.GetFuncName() + "-' '" err := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) { req := busi.GenTransReq(30, false, false) diff --git a/test/xa_test.go b/test/xa_test.go index 4980a0a..a15c339 100644 --- a/test/xa_test.go +++ b/test/xa_test.go @@ -43,10 +43,10 @@ func TestXaDuplicate(t *testing.T) { sdb, err := dtmimp.StandaloneDB(busi.BusiConf) assert.Nil(t, err) if dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql { - _, err = dtmimp.DBExec(sdb, "xa recover") + _, err = dtmimp.DBExec(busi.BusiConf.Driver, sdb, "xa recover") assert.Nil(t, err) } - _, err = dtmimp.DBExec(sdb, dtmimp.GetDBSpecial().GetXaSQL("commit", gid+"-01")) // simulate repeated request + _, err = dtmimp.DBExec(busi.BusiConf.Driver, sdb, dtmimp.GetDBSpecial(busi.BusiConf.Driver).GetXaSQL("commit", gid+"-01")) // simulate repeated request assert.Nil(t, err) return xa.CallBranch(req, busi.Busi+"/TransInXa") }) @@ -94,6 +94,7 @@ func TestXaTimeout(t *testing.T) { }) assert.Error(t, err) assert.Equal(t, StatusFailed, getTransStatus(gid)) + assert.Regexp(t, `^Timeout after \d+ seconds$`, getTrans(gid).RollbackReason) assert.Equal(t, []string{}, getBranchesStatus(gid)) }